Skip to content

CloudFront distributions should have field level encryption enabled

Field-level encryption protects specific sensitive fields (credit card numbers, personal identifiers) at the CloudFront edge using asymmetric encryption before the data reaches your origin. Without it, HTTPS terminates at the origin and the plaintext payload is visible to every component in the processing chain. Field-level encryption ensures designated fields remain encrypted end-to-end, readable only by services holding the corresponding private key.

This narrows the blast radius of a compromised origin or intermediate service. Even if an attacker gains access to application logs, caches, or databases that store the full request body, the protected fields stay ciphertext.

Retrofit consideration

Enabling field-level encryption requires generating an RSA key pair, creating an encryption profile mapping specific POST body fields, and updating your origin application to decrypt those fields. This changes the request payload format your backend receives, which will break any service that reads the protected fields without decrypting them first.

Implementation

Choose the approach that matches how you manage Terraform.

If you use terraform-aws-modules/cloudfront/aws, set the right module inputs for this control. You can later migrate to the compliance.tf module with minimal changes because it is compatible by design.

module "cloudfront" {
  source  = "terraform-aws-modules/cloudfront/aws"
  version = ">=6.0.0,<7.0.0"
}

Use AWS provider resources directly. See docs for the resources involved: aws_cloudfront_distribution.

resource "aws_cloudfront_distribution" "this" {
  default_cache_behavior {
    allowed_methods           = ["GET", "HEAD"]
    cached_methods            = ["GET", "HEAD"]
    field_level_encryption_id = "abc123"

    forwarded_values {
      cookies {
        forward = "none"
      }
      query_string = false
    }

    target_origin_id       = "S3Origin"
    viewer_protocol_policy = "redirect-to-https"
  }
  default_root_object = "index.html"
  enabled             = true

  logging_config {
    bucket = "example-bucket-abc123_domain_name"
  }

  origin {
    domain_name = "example.s3.amazonaws.com"
    origin_id   = "S3Origin"
  }

  restrictions {
    geo_restriction {
      locations        = ["US", "CA", "GB"]
      restriction_type = "whitelist"
    }
  }

  viewer_certificate {
    acm_certificate_arn      = "arn:aws:acm:us-east-1:123456789012:certificate/12345678-1234-1234-1234-123456789012"
    minimum_protocol_version = "TLSv1.2_2021"
    ssl_support_method       = "sni-only"
  }

  wait_for_deployment = false
  web_acl_id          = "arn:aws:wafv2:us-east-1:123456789012:regional/webacl/example/12345678-1234-1234-1234-123456789012"
}

What this control checks

The aws_cloudfront_distribution resource must reference a field-level encryption configuration in its cache behaviors. Start by creating an aws_cloudfront_public_key with your RSA public key PEM, then an aws_cloudfront_field_level_encryption_profile that maps specific POST body field names to that key. Create an aws_cloudfront_field_level_encryption_config referencing the profile and specifying the content type to encrypt. Set field_level_encryption_id on default_cache_behavior and any ordered_cache_behavior blocks to that config's ID. A distribution fails if field_level_encryption_id is empty or unset on any cache behavior.

Common pitfalls

  • Only POST body fields are eligible

    Field-level encryption works on specific fields in POST request bodies. It does not encrypt query string parameters, headers, or GET request data. If your sensitive data arrives through those channels, this control alone does not protect it.

  • RSA key size and rotation

    The aws_cloudfront_public_key resource requires an RSA public key in PEM format; CloudFront supports 2048-bit keys. Key rotation is manual. Replacing the public key means updating the encryption profile and re-deploying the distribution, which can cause brief disruption.

  • Origin must handle decryption

    Your origin application receives the designated fields as ciphertext (base64-encoded). If you enable field-level encryption without updating your backend to decrypt those fields using the corresponding private key, your application will break. Test in staging first.

  • Ordered cache behaviors may be missed

    Setting field_level_encryption_id on default_cache_behavior alone is insufficient if you have ordered_cache_behavior blocks that handle routes receiving sensitive POST data. Each behavior that processes sensitive fields needs its own field_level_encryption_id.

Audit evidence

Config rule evaluations showing field-level encryption enabled on CloudFront distributions, or equivalent CSPM findings. Supporting evidence includes the CloudFront console view of each cache behavior's encryption configuration ID and the encryption profile listing which POST body fields are encrypted and which public key is assigned.

CloudTrail logs for CreateFieldLevelEncryptionConfig and UpdateDistribution API calls show when encryption was configured and by whom. If a data classification policy specifies which fields are sensitive, the auditor may cross-reference the encryption profile's field list against that policy to confirm coverage.

Framework-specific interpretation

Tool mappings

Use these identifiers to cross-reference this control across tools, reports, and evidence.

  • Compliance.tf Control: cloudfront_distribution_field_level_encryption_enabled

  • Powerpipe Control: aws_compliance.control.cloudfront_distribution_field_level_encryption_enabled

  • Prowler Check: cloudfront_distributions_field_level_encryption_enabled

Last reviewed: 2026-03-09