Skip to content

CloudFront distributions should use the recommended TLS security policy

CloudFront distributions that allow TLS 1.0 or 1.1 expose traffic to known protocol weaknesses including BEAST, POODLE, and downgrade attacks. Both versions were formally deprecated by RFC 8996 in March 2021, and major browsers have dropped support for them. Setting TLSv1.2_2021 as the minimum restricts connections to TLS 1.2 with strong ciphers, eliminating CBC-mode cipher suites that have been repeatedly targeted.

Running an older policy also creates friction during audits and penetration tests, where anything below TLS 1.2 gets flagged as a finding regardless of compensating controls. Enforcing the policy at the distribution level avoids that overhead entirely.

Retrofit consideration

Changing the minimum TLS policy can break clients that don't support TLS 1.2, including very old Android devices and custom HTTP libraries compiled against outdated OpenSSL versions. Check CloudFront access logs for TLS version distribution before switching.

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"

  viewer_certificate = {
    minimum_protocol_version = "TLSv1.2_2021"
  }
}

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"
    }
  }

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

  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"
  }
}

What this control checks

In the aws_cloudfront_distribution resource, viewer_certificate must set minimum_protocol_version to "TLSv1.2_2021" or a newer policy. "TLSv1.2_2019" is older and fails this control because it still permits weaker cipher suites. When cloudfront_default_certificate is true (the *.cloudfront.net domain), AWS manages the TLS policy and minimum_protocol_version is silently ignored, which may still cause a failure depending on the evaluator. For a reliable pass, use a custom ACM certificate: set acm_certificate_arn, ssl_support_method = "sni-only", and minimum_protocol_version = "TLSv1.2_2021". Any of the following values fail: "TLSv1", "TLSv1_2016", "TLSv1.1_2016", "TLSv1.2_2018", or an omitted minimum_protocol_version.

Common pitfalls

  • Default certificate ignores minimum_protocol_version

    Using cloudfront_default_certificate = true hands TLS policy control to AWS, so minimum_protocol_version is silently ignored. Distributions using the shared *.cloudfront.net certificate may still fail this control. Switch to a custom ACM certificate with acm_certificate_arn to explicitly own the policy setting.

  • Confusing policy name ordering

    All policies named TLSv1.2_* require TLS 1.2, but they differ on cipher suites. TLSv1.2_2018 and TLSv1.2_2019 both still permit CBC ciphers. TLSv1.2_2021 is the first in the series to remove them entirely. The year suffix matters.

  • Legacy vip ssl_support_method requires dedicated IP

    The "vip" value for ssl_support_method provisions a dedicated IP address for SSL and incurs extra CloudFront charges, but has no effect on which TLS version is negotiated. It's a common mistake when first adding a custom certificate: old Terraform snippets often specify "vip" without flagging the cost implication. Use "sni-only" unless you have a specific reason not to.

  • Terraform default value may not match recommended policy

    Omit minimum_protocol_version and behavior falls back to AWS defaults, which vary by certificate mode and may not satisfy this control. Always set it explicitly in the viewer_certificate block.

Audit evidence

Auditors expect Config rule evaluation results showing each distribution as COMPLIANT, or Security Hub findings with status PASSED for the relevant CloudFront controls. The CloudFront console Security Policy column should list TLSv1.2_2021 or later for every distribution. The CLI command aws cloudfront get-distribution-config --id <ID> returns ViewerCertificate.MinimumProtocolVersion for point-in-time verification. Historical compliance comes from Config timeline snapshots or periodic Security Hub exports stored in S3.

Tool mappings

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

  • Compliance.tf Control: cloudfront_distribution_uses_recommended_tls_security_policy

  • AWS Config Managed Rules: CLOUDFRONT_SECURITY_POLICY_CHECK, CLOUDFRONT_SSL_POLICY_CHECK

  • Checkov Check: CKV_AWS_174

  • Powerpipe Control: aws_compliance.control.cloudfront_distribution_uses_recommended_tls_security_policy

  • AWS Security Hub Control: CloudFront.15

  • KICS Queries: 00e5e55e-c2ff-46b3-a757-a7a1cd802456, 3a1e94df-6847-4c0e-a3b6-6c6af4e128ef

  • Trivy Check: AWS-0013

Last reviewed: 2026-03-09