Skip to main content
emnode / learn
Compliance Medium severity

AWS Security Hub · CloudFront

CloudFront.15: Distributions should use recommended TLS policy

Written and reviewed by Emnode · Last reviewed

What does AWS Security Hub CloudFront.15 check?

CloudFront.15 checks that a distribution's viewer certificate uses a recommended `MinimumProtocolVersion` — `TLSv1.2_2021`, `TLSv1.2_2025`, or `TLSv1.3_2025`. It reports FAILED for anything older. It only evaluates distributions using a custom SSL certificate served via SNI, not the default `*.cloudfront.net` certificate or dedicated-IP SSL.

Why does CloudFront.15 matter?

An old security policy like `TLSv1` or `TLSv1.1_2016` completes handshakes using protocols the IETF deprecated in 2021 and ciphers that no longer meet modern standards. An attacker on the network path can attempt to force a connection down to the weakest mutually-supported protocol; raising the floor removes that room. It maps to PCI DSS and the NIST SC-8/SC-23 data-in-transit families that auditors and vendor questionnaires routinely scan for.

How do I fix CloudFront.15?

  1. Inventory each distribution's `MinimumProtocolVersion` and flag any below the recommended floor.
  2. Confirm no flagged distribution genuinely needs pre-TLS-1.2 clients (a non-issue for the SNI-served, public distributions the control targets).
  3. Set `MinimumProtocolVersion` under `ViewerCertificate` to a recommended value via the read-modify-write `update-distribution` flow — send the whole config, not a fragment, or you reset other fields.
  4. Pin the policy in IaC and watch for drift so distributions can't regress to an old default.

Remediation script · bash

# 1. Lock an S3 origin: only THIS distribution may read the bucket.
cat > bucket-policy.json <<'JSON'
{
  "Version": "2012-10-17",
  "Statement": [{
    "Sid": "AllowCloudFrontServicePrincipalReadOnly",
    "Effect": "Allow",
    "Principal": { "Service": "cloudfront.amazonaws.com" },
    "Action": "s3:GetObject",
    "Resource": "arn:aws:s3:::my-downloads-bucket/*",
    "Condition": { "StringEquals": {
      "AWS:SourceArn": "arn:aws:cloudfront::111122223333:distribution/E2QWRUHAPOMQZL"
    } }
  }]
}
JSON
aws s3api put-bucket-policy --bucket my-downloads-bucket --policy file://bucket-policy.json

# 2. Raise the minimum TLS version (the field is nested in ViewerCertificate, so send the whole config back).
aws cloudfront get-distribution-config --id E2QWRUHAPOMQZL > dist.json
ETAG=$(python3 -c "import json;print(json.load(open('dist.json'))['ETag'])")
# ... edit dist.json: ViewerCertificate.MinimumProtocolVersion = TLSv1.2_2021, ViewerProtocolPolicy = redirect-to-https ...
aws cloudfront update-distribution --id E2QWRUHAPOMQZL \
  --distribution-config file://distribution-config.json --if-match "$ETAG"

# 3. Confirm the second door is shut: a raw S3 GET should now return 403.
curl -s -o /dev/null -w '%{http_code}\n' https://my-downloads-bucket.s3.amazonaws.com/file.pdf

Full walkthrough (console steps, edge cases and verification) in the lesson Protect CloudFront distributions and origins.

Is CloudFront.15 a false positive?

Distributions on the default CloudFront certificate, or using dedicated-IP SSL for legacy clients, don't generate findings — CloudFront pins the policy itself in those cases, so the setting isn't yours to change.

Part of the learning path Lock down access