CloudFront Distributions Field-Level Encryption Enabled
Overview
This check verifies that Amazon CloudFront distributions have Field-Level Encryption (FLE) enabled. Field-level encryption adds an extra layer of security beyond HTTPS by encrypting specific sensitive data fields (like credit card numbers or personal information) at the edge location, close to the user. This encrypted data stays encrypted as it moves through your application stack and can only be decrypted by applications that have the private key.
Risk
Without field-level encryption, sensitive data in your web forms may be exposed in several places:
- Origin server logs: Sensitive fields like passwords or payment data could appear in plaintext in application logs
- Middleware exposure: Data passing through load balancers, proxies, or caching layers remains readable
- Insider threats: Anyone with access to origin servers can view sensitive user inputs
- Data breaches: If your origin is compromised, all user-submitted data could be stolen in cleartext
Field-level encryption ensures that even if attackers gain access to your backend systems, they cannot read the encrypted sensitive fields without the private key.
Remediation Steps
Prerequisites
- Access to the AWS Console with permissions to manage CloudFront distributions
- An RSA key pair (2048-bit minimum) for encryption/decryption
- Your application must be able to decrypt data using the AWS Encryption SDK
Required IAM permissions
To configure field-level encryption, you need these permissions:
cloudfront:CreatePublicKeycloudfront:CreateFieldLevelEncryptionProfilecloudfront:CreateFieldLevelEncryptionConfigcloudfront:GetDistributioncloudfront:GetDistributionConfigcloudfront:UpdateDistribution
Generate an RSA key pair
If you don't have an RSA key pair, generate one using OpenSSL:
# Generate a 2048-bit RSA private key
openssl genrsa -out private_key.pem 2048
# Extract the public key
openssl rsa -pubout -in private_key.pem -out public_key.pem
Important: Store the private key securely (e.g., in AWS Secrets Manager or a hardware security module). You'll need it to decrypt data at your origin.
AWS Console Method
Field-level encryption setup requires four steps: adding a public key, creating an encryption profile, creating a configuration, and attaching it to your distribution.
Step 1: Add Your Public Key
- Open the CloudFront console
- In the left navigation, click Public keys
- Click Create public key
- Enter a Name (e.g.,
my-fle-public-key) - Paste your public key contents (including the
-----BEGIN PUBLIC KEY-----and-----END PUBLIC KEY-----lines) - Optionally add a Comment with an expiration date for key rotation tracking
- Click Create public key
Step 2: Create a Field-Level Encryption Profile
- In the left navigation, click Field-level encryption
- Under the Profiles tab, click Create profile
- Enter a Profile name (e.g.,
sensitive-data-profile) - Select your Public key from the dropdown
- Enter a Provider name (e.g.,
MY_PROVIDER) - you'll use this in your decryption code - Under Field patterns, add the field names you want to encrypt:
- Click Add field pattern
- Enter the field name exactly as it appears in your form (e.g.,
creditCardNumber,ssn) - You can use wildcards (e.g.,
card*to matchcardNumber,cardCVV)
- Click Create profile
Step 3: Create a Field-Level Encryption Configuration
- Still on the Field-level encryption page, click the Configurations tab
- Click Create configuration
- Add a content type mapping:
- Content type:
application/x-www-form-urlencoded(this is fixed) - Default profile ID: Select the profile you created
- Content type:
- Click Create configuration
- Note the Configuration ID - you'll need it for the next step
Step 4: Attach Configuration to Your Distribution
- Go to Distributions and click on your distribution ID
- Click the Behaviors tab
- Select the behavior that handles your sensitive form submissions and click Edit
- Verify these settings (required for FLE):
- Viewer protocol policy: Must be
Redirect HTTP to HTTPSorHTTPS only - Allowed HTTP methods: Must include POST (select
GET, HEAD, OPTIONS, PUT, POST, PATCH, DELETE)
- Viewer protocol policy: Must be
- Under Field-level encryption, select your configuration from the dropdown
- Click Save changes
- Wait for the distribution status to change from "Deploying" to "Deployed"
AWS CLI (optional)
Setting up field-level encryption via CLI requires multiple steps. Here's the complete process:
Step 1: Create a public key
First, prepare a JSON file with your public key:
# Read your public key and escape for JSON
PUBLIC_KEY=$(cat public_key.pem)
# Create the request JSON
cat > create-public-key.json << 'EOF'
{
"PublicKeyConfig": {
"CallerReference": "my-fle-key-ref-001",
"Name": "my-fle-public-key",
"EncodedKey": "<paste-your-public-key-here>",
"Comment": "Public key for field-level encryption"
}
}
EOF
Edit create-public-key.json and replace <paste-your-public-key-here> with your actual public key content.
aws cloudfront create-public-key \
--public-key-config file://create-public-key.json \
--region us-east-1
Save the returned Id value as PUBLIC_KEY_ID.
Step 2: Create a field-level encryption profile
cat > create-fle-profile.json << 'EOF'
{
"FieldLevelEncryptionProfileConfig": {
"Name": "sensitive-data-profile",
"CallerReference": "my-fle-profile-ref-001",
"EncryptionEntities": {
"Quantity": 1,
"Items": [
{
"PublicKeyId": "<PUBLIC_KEY_ID>",
"ProviderId": "MY_PROVIDER",
"FieldPatterns": {
"Quantity": 2,
"Items": ["creditCardNumber", "ssn"]
}
}
]
},
"Comment": "Profile for encrypting sensitive form fields"
}
}
EOF
Replace <PUBLIC_KEY_ID> with the ID from step 1.
aws cloudfront create-field-level-encryption-profile \
--field-level-encryption-profile-config file://create-fle-profile.json \
--region us-east-1
Save the returned Id value as PROFILE_ID.
Step 3: Create a field-level encryption configuration
cat > create-fle-config.json << 'EOF'
{
"FieldLevelEncryptionConfig": {
"CallerReference": "my-fle-config-ref-001",
"Comment": "FLE configuration for sensitive data",
"ContentTypeProfileConfig": {
"ForwardWhenContentTypeIsUnknown": false,
"ContentTypeProfiles": {
"Quantity": 1,
"Items": [
{
"Format": "URLEncoded",
"ProfileId": "<PROFILE_ID>",
"ContentType": "application/x-www-form-urlencoded"
}
]
}
},
"QueryArgProfileConfig": {
"ForwardWhenQueryArgProfileIsUnknown": false,
"QueryArgProfiles": {
"Quantity": 0
}
}
}
}
EOF
Replace <PROFILE_ID> with the ID from step 2.
aws cloudfront create-field-level-encryption-config \
--field-level-encryption-config file://create-fle-config.json \
--region us-east-1
Save the returned Id value as FLE_CONFIG_ID.
Step 4: Update your distribution to use the configuration
# Get current distribution config
aws cloudfront get-distribution-config \
--id <DISTRIBUTION_ID> \
--region us-east-1 \
> dist-config.json
# Extract the ETag
ETAG=$(cat dist-config.json | jq -r '.ETag')
# Modify the config to add field-level encryption
# Extract just the DistributionConfig and add the FLE ID
cat dist-config.json | jq '.DistributionConfig | .DefaultCacheBehavior.FieldLevelEncryptionId = "<FLE_CONFIG_ID>"' > updated-config.json
# Update the distribution
aws cloudfront update-distribution \
--id <DISTRIBUTION_ID> \
--if-match "$ETAG" \
--distribution-config file://updated-config.json \
--region us-east-1
Replace <DISTRIBUTION_ID> with your CloudFront distribution ID and <FLE_CONFIG_ID> with the configuration ID from step 3.
CloudFormation (optional)
AWSTemplateFormatVersion: '2010-09-09'
Description: CloudFront distribution with field-level encryption enabled
Parameters:
OriginDomainName:
Type: String
Description: The domain name of your origin server
PublicKeyEncodedValue:
Type: String
Description: The PEM-encoded RSA public key (including BEGIN/END lines)
NoEcho: true
SensitiveFieldPatterns:
Type: CommaDelimitedList
Default: "creditCardNumber,ssn"
Description: Comma-separated list of field names to encrypt
Resources:
FLEPublicKey:
Type: AWS::CloudFront::PublicKey
Properties:
PublicKeyConfig:
CallerReference: !Sub "${AWS::StackName}-public-key-ref"
Name: !Sub "${AWS::StackName}-fle-public-key"
EncodedKey: !Ref PublicKeyEncodedValue
Comment: "Public key for field-level encryption"
FLEProfile:
Type: AWS::CloudFront::FieldLevelEncryptionProfile
Properties:
FieldLevelEncryptionProfileConfig:
Name: !Sub "${AWS::StackName}-fle-profile"
CallerReference: !Sub "${AWS::StackName}-profile-ref"
Comment: "Profile for encrypting sensitive form fields"
EncryptionEntities:
Items:
- PublicKeyId: !Ref FLEPublicKey
ProviderId: "MY_PROVIDER"
FieldPatterns:
Items: !Ref SensitiveFieldPatterns
FLEConfig:
Type: AWS::CloudFront::FieldLevelEncryptionConfig
Properties:
FieldLevelEncryptionConfig:
Comment: "FLE configuration for sensitive data"
ContentTypeProfileConfig:
ForwardWhenContentTypeIsUnknown: false
ContentTypeProfiles:
Items:
- ContentType: "application/x-www-form-urlencoded"
Format: "URLEncoded"
ProfileId: !Ref FLEProfile
QueryArgProfileConfig:
ForwardWhenQueryArgProfileIsUnknown: false
CloudFrontDistribution:
Type: AWS::CloudFront::Distribution
Properties:
DistributionConfig:
Enabled: true
Comment: "Distribution with field-level encryption"
DefaultRootObject: index.html
Origins:
- Id: PrimaryOrigin
DomainName: !Ref OriginDomainName
CustomOriginConfig:
HTTPPort: 80
HTTPSPort: 443
OriginProtocolPolicy: https-only
OriginSSLProtocols:
- TLSv1.2
DefaultCacheBehavior:
TargetOriginId: PrimaryOrigin
ViewerProtocolPolicy: redirect-to-https
AllowedMethods:
- GET
- HEAD
- OPTIONS
- PUT
- POST
- PATCH
- DELETE
CachedMethods:
- GET
- HEAD
FieldLevelEncryptionId: !Ref FLEConfig
ForwardedValues:
QueryString: true
Cookies:
Forward: all
Compress: true
ViewerCertificate:
CloudFrontDefaultCertificate: true
Outputs:
DistributionId:
Description: CloudFront Distribution ID
Value: !Ref CloudFrontDistribution
DistributionDomainName:
Description: CloudFront Distribution Domain Name
Value: !GetAtt CloudFrontDistribution.DomainName
FLEConfigId:
Description: Field-Level Encryption Configuration ID
Value: !Ref FLEConfig
Deploy with:
aws cloudformation deploy \
--template-file cloudfront-fle.yaml \
--stack-name cloudfront-fle-distribution \
--parameter-overrides \
OriginDomainName=my-origin.example.com \
PublicKeyEncodedValue="$(cat public_key.pem)" \
SensitiveFieldPatterns="creditCardNumber,ssn,dateOfBirth" \
--region us-east-1
Terraform (optional)
# variables.tf
variable "origin_domain_name" {
description = "The domain name of your origin server"
type = string
}
variable "public_key_pem" {
description = "Path to the PEM-encoded public key file"
type = string
}
variable "sensitive_field_patterns" {
description = "List of field names/patterns to encrypt"
type = list(string)
default = ["creditCardNumber", "ssn"]
}
variable "provider_id" {
description = "Provider ID for decryption (used in your origin application)"
type = string
default = "MY_PROVIDER"
}
# main.tf
resource "aws_cloudfront_public_key" "fle_key" {
name = "fle-public-key"
comment = "Public key for field-level encryption"
encoded_key = file(var.public_key_pem)
}
resource "aws_cloudfront_field_level_encryption_profile" "fle_profile" {
name = "sensitive-data-profile"
comment = "Profile for encrypting sensitive form fields"
encryption_entities {
items {
public_key_id = aws_cloudfront_public_key.fle_key.id
provider_id = var.provider_id
field_patterns {
items = var.sensitive_field_patterns
}
}
}
}
resource "aws_cloudfront_field_level_encryption_config" "fle_config" {
comment = "FLE configuration for sensitive data"
content_type_profile_config {
forward_when_content_type_is_unknown = false
content_type_profiles {
items {
content_type = "application/x-www-form-urlencoded"
format = "URLEncoded"
profile_id = aws_cloudfront_field_level_encryption_profile.fle_profile.id
}
}
}
query_arg_profile_config {
forward_when_query_arg_profile_is_unknown = false
}
}
resource "aws_cloudfront_distribution" "main" {
enabled = true
comment = "Distribution with field-level encryption"
default_root_object = "index.html"
origin {
domain_name = var.origin_domain_name
origin_id = "primary-origin"
custom_origin_config {
http_port = 80
https_port = 443
origin_protocol_policy = "https-only"
origin_ssl_protocols = ["TLSv1.2"]
}
}
default_cache_behavior {
target_origin_id = "primary-origin"
# Required settings for field-level encryption
viewer_protocol_policy = "redirect-to-https"
allowed_methods = ["GET", "HEAD", "OPTIONS", "PUT", "POST", "PATCH", "DELETE"]
cached_methods = ["GET", "HEAD"]
# Attach the field-level encryption configuration
field_level_encryption_id = aws_cloudfront_field_level_encryption_config.fle_config.id
forwarded_values {
query_string = true
cookies {
forward = "all"
}
}
compress = true
}
restrictions {
geo_restriction {
restriction_type = "none"
}
}
viewer_certificate {
cloudfront_default_certificate = true
}
tags = {
Environment = "production"
}
}
# outputs.tf
output "distribution_id" {
description = "CloudFront Distribution ID"
value = aws_cloudfront_distribution.main.id
}
output "distribution_domain_name" {
description = "CloudFront Distribution Domain Name"
value = aws_cloudfront_distribution.main.domain_name
}
output "fle_config_id" {
description = "Field-Level Encryption Configuration ID"
value = aws_cloudfront_field_level_encryption_config.fle_config.id
}
Deploy with:
terraform init
terraform apply \
-var="origin_domain_name=my-origin.example.com" \
-var="public_key_pem=./public_key.pem" \
-var='sensitive_field_patterns=["creditCardNumber", "ssn", "dateOfBirth"]'
Verification
After enabling field-level encryption:
- Go to the CloudFront console
- Click on your distribution
- Go to the Behaviors tab
- Click on your behavior and verify that Field-level encryption shows your configuration
To test the encryption is working:
- Submit a form through your CloudFront distribution with a field that should be encrypted
- Check your origin server logs - the encrypted field should appear as a long base64-encoded string, not the original value
CLI verification
Check if FLE is configured on your distribution:
aws cloudfront get-distribution \
--id <DISTRIBUTION_ID> \
--query 'Distribution.DistributionConfig.DefaultCacheBehavior.FieldLevelEncryptionId' \
--output text \
--region us-east-1
Expected output: Your FLE configuration ID (not empty or None)
List all FLE configurations in your account:
aws cloudfront list-field-level-encryption-configs \
--region us-east-1
Re-run the Prowler check:
prowler aws --checks cloudfront_distributions_field_level_encryption_enabled
Additional Resources
- Using Field-Level Encryption to Help Protect Sensitive Data
- Setting Up Field-Level Encryption
- Decrypting Data Fields at Your Origin
- AWS Encryption SDK
- CloudFront Security Best Practices
Notes
-
Application changes required: Your origin application must be updated to decrypt the encrypted fields using the AWS Encryption SDK and your private key. This is not just a CloudFront configuration change.
-
Supported content types: Field-level encryption only works with
application/x-www-form-urlencodedcontent type. JSON payloads are not supported. -
Field limits: You can encrypt up to 10 fields per request.
-
HTTPS required: Field-level encryption requires HTTPS between viewers and CloudFront. Ensure your viewer protocol policy is set to
redirect-to-httpsorhttps-only. -
HTTP methods: The distribution must allow POST/PUT methods for forms to be submitted. Field-level encryption applies to request body data.
-
Key rotation: Plan for key rotation by including expiration dates in your public key comments. When rotating, create a new key/profile before removing the old one.
-
Private key security: Store your private key in a secure location like AWS Secrets Manager or a hardware security module (HSM). Never commit it to source control.
-
Not a replacement for encryption at rest: Field-level encryption protects data in transit and processing. You should still encrypt sensitive data at rest in your databases.
-
Deployment time: Changes to CloudFront distributions take 5-15 minutes to propagate globally.
-
Cost considerations: Field-level encryption does not have additional charges beyond standard CloudFront pricing, but the encryption/decryption processing may slightly increase latency.