Get started

OpenSourceOM Team ·

Amazon CloudFront Security: OAC, TLS, and Origin Lockdown

Amazon CloudFront security that actually holds: Origin Access Control instead of OAI, HTTPS only, S3 bucket policies that deny non-CloudFront, WAF attachment, and the bypass of hitting the bucket URL directly.

  • AWS
  • CloudFront
  • S3
  • WAF
  • TLS

Viewers hit d111111abcdef8.cloudfront.net (or your alias). The origin is still an S3 bucket, an ALB, or a custom host. Amazon CloudFront security is making the distribution the only door: Origin Access Control, HTTPS, and a WAF that actually sits on that door. It is not CloudFront Functions fan-out, not a CDN performance guide, and not Cloud Armor (that is GCP). AWS reference: Restricting access to an origin.

Viewer
  → CloudFront (TLS, WAF, cache)
       → OAC sigv4   → S3 (bucket policy)
       → or HTTPS    → ALB (SG: CloudFront prefix list only)

If bucket.s3.amazonaws.com/secret.pdf still 200s, the distribution is a convenience URL, not a control.

1. Origin Access Control, not a public bucket

OAI (legacy canonical user) still works. OAC is the current control: CloudFront signs GetObject with SigV4, and the bucket policy allows that service principal for this distribution.

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "AllowCloudFrontOAC",
      "Effect": "Allow",
      "Principal": { "Service": "cloudfront.amazonaws.com" },
      "Action": "s3:GetObject",
      "Resource": "arn:aws:s3:::payments-web/*",
      "Condition": {
        "StringEquals": {
          "AWS:SourceArn": "arn:aws:cloudfront::123456789012:distribution/EDFDVBD6EXAMPLE"
        }
      }
    }
  ]
}

Block Public Access on the bucket stays on. Website hosting on the bucket stays off unless you have a written exception—the website endpoint ignores this policy model.

aws s3api get-bucket-policy --bucket payments-web
aws cloudfront get-distribution-config --id EDFDVBD6EXAMPLE \
  --query 'DistributionConfig.Origins.Items[].OriginAccessControlId'

Empty OriginAccessControlId and a public ACL is the classic miss. Migrate OAI → OAC with AWS’s documented cutover; do not delete OAI before OAC is serving.

2. Viewer TLS and origin protocol

Setting Prod default
ViewerProtocolPolicy redirect-to-https or https-only
MinimumProtocolVersion TLSv1.2_2021 (or current AWS recommended)
Origin OriginProtocolPolicy https-only for ALB/custom; S3 REST uses OAC not HTTP origin

Custom domains need an ACM certificate in us-east-1 for CloudFront. Alternate domain names without a matching cert fail; do not “fix” that by serving the default cloudfront.net name in production emails.

Failure mode: allow-all viewer policy so a partner’s HTTP health check works. Attackers use HTTP too.

3. Lock ALB origins to CloudFront

For an ALB origin, OAC does not apply. Use the CloudFront managed prefix list (com.amazonaws.global.cloudfront.origin-facing) on the ALB security group, and a custom header CloudFront injects that the ALB listener rule requires (or AWS’s origin verify with a secret header). Prefix list alone is necessary but not sufficient if someone copies the prefix list onto another attacker-controlled distribution.

aws ec2 describe-managed-prefix-lists \
  --filters Name=prefix-list-name,Values=com.amazonaws.global.cloudfront.origin-facing

If the ALB SG still allows 0.0.0.0/0 on 443, users and scanners skip CloudFront, skip WAF, and skip your cache behaviors.

4. WAF on the distribution

Associate a WAFv2 Web ACL with the distribution, not only with the ALB.

aws wafv2 list-web-acls --scope CLOUDFRONT --region us-east-1

CloudFront WAF is us-east-1 / global. An ACL on the ALB in eu-west-1 does not inspect viewers who never reach the ALB because of a cache hit—and does not inspect viewers who hit the ALB directly if you failed step 3.

Start with AWS managed common rule set in count, then block. Rate-limit /login here; application quotas still belong in the origin (cloud-native application security).

5. Logging and signed URLs

  • Standard logs (legacy) or v2 access logs to a dedicated bucket with Block Public Access. The log bucket must not be the origin bucket.
  • Signed cookies/URLs for non-public objects: trusted key groups, short TTL, no * in Resource for the canned policy if you can avoid it.

Field-level encryption is a niche PCI control; do not enable it because a checklist said “encryption.” It breaks caching and origin parsers if you do not own the private key path.

Checklist

  • OAC (or documented OAI) on every S3 origin; bucket policy scoped to that distribution ARN
  • S3 Block Public Access on; no static website endpoint for private content
  • Viewer HTTPS-only or redirect; TLS 1.2+ policy
  • ALB SG = CloudFront prefix list + origin secret header (or equivalent)
  • WAFv2 associated on the distribution in us-east-1
  • Access logs in a separate locked bucket
  • Direct origin URL returns 403

A CloudFront distribution with a public S3 origin is still an internet-reachable object store. Rank leftover public objects with attack path analysis.

Related: AWS security best practices · Cloud-native application security · GCP Cloud Armor