为AWS托管内部CloudFront的API添加HSTS安全头方案咨询
解决方案:为Edge-Optimized API Gateway添加HSTS等安全头
核心限制说明
Edge-Optimized API Gateway自动创建的CloudFront分发由AWS托管,用户无法直接通过Terraform修改该分发的响应头策略——因为这个分发不在你的Terraform状态管理范围内,也不允许直接编辑其配置。
可行解决方案
方案1:改用自定义域名 + 自行管理CloudFront分发(推荐)
这是最可控的方案,完全通过Terraform管理整个链路,步骤如下:
- 为API Gateway创建自定义域名,获取其区域目标域名;
- 自行创建CloudFront分发,将源指向API Gateway的自定义域名目标;
- 创建CloudFront响应头策略,配置HSTS、XSS保护等安全头;
- 将响应头策略关联到CloudFront的缓存行为中。
Terraform代码示例:
# 创建CloudFront响应头策略 resource "aws_cloudfront_response_headers_policy" "security_headers" { name = "api-security-headers" strict_transport_security { access_control_max_age_sec = 31536000 include_subdomains = true preload = true override = true } xss_protection { mode_block = true protection = true override = true } content_security_policy { content_security_policy = "default-src 'self'; script-src 'self'" override = true } # 可按需添加其他安全头,如X-Frame-Options、X-Content-Type-Options等 } # 创建CloudFront分发,关联API Gateway自定义域名 resource "aws_cloudfront_distribution" "api_distribution" { enabled = true is_ipv6_enabled = true origin { domain_name = aws_api_gateway_domain_name.api_custom.regional_domain_name origin_id = "api-gateway-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 = "api-gateway-origin" viewer_protocol_policy = "redirect-to-https" response_headers_policy_id = aws_cloudfront_response_headers_policy.security_headers.id allowed_methods = ["GET", "HEAD", "OPTIONS", "PUT", "POST", "PATCH", "DELETE"] cached_methods = ["GET", "HEAD"] } # 配置自定义域名证书与TLS版本 viewer_certificate { acm_certificate_arn = aws_acm_certificate.api_cert.arn ssl_support_method = "sni-only" minimum_protocol_version = "TLSv1.2" } }
方案2:直接在API Gateway层面配置响应头
如果不想更换CloudFront分发,可以在API Gateway的方法响应或集成响应中添加安全头,CloudFront会自动转发这些头到客户端。
Terraform代码示例(为单个API方法配置安全头):
# 在方法响应中声明要返回的安全头 resource "aws_api_gateway_method_response" "example" { rest_api_id = aws_api_gateway_rest_api.example.id resource_id = aws_api_gateway_resource.example.id http_method = aws_api_gateway_method.example.http_method status_code = "200" response_parameters = { "method.response.header.Strict-Transport-Security" = true "method.response.header.X-XSS-Protection" = true } } # 在集成响应中设置头的具体值 resource "aws_api_gateway_integration_response" "example" { rest_api_id = aws_api_gateway_rest_api.example.id resource_id = aws_api_gateway_resource.example.id http_method = aws_api_gateway_method.example.http_method status_code = aws_api_gateway_method_response.example.status_code response_parameters = { "method.response.header.Strict-Transport-Security" = "'max-age=31536000; includeSubDomains; preload'" "method.response.header.X-XSS-Protection" = "'1; mode=block'" } }
注意:这种方式需要为每个API方法单独配置,适合API数量较少的场景。
方案3:使用Lambda@Edge添加安全头
通过Lambda@Edge在CloudFront的响应阶段注入安全头,无需更换现有域名:
- 创建部署在us-east-1区域的Lambda函数,在
origin-response事件中添加安全头; - 将Lambda函数关联到API Gateway自动创建的CloudFront分发上。
Lambda函数代码示例(Node.js):
exports.handler = (event, context, callback) => { const response = event.Records[0].cf.response; const headers = response.headers; // 添加HSTS头 headers['strict-transport-security'] = [{ key: 'Strict-Transport-Security', value: 'max-age=31536000; includeSubDomains; preload' }]; // 添加XSS保护头 headers['x-xss-protection'] = [{ key: 'X-XSS-Protection', value: '1; mode=block' }]; callback(null, response); };
Terraform代码示例(部署Lambda@Edge并关联到CloudFront):
# 获取API Gateway自动创建的CloudFront分发(通过执行域名查找) data "aws_cloudfront_distribution" "api_gateway_dist" { domain_name = aws_api_gateway_rest_api.example.execute_api_domain_name } # 创建Lambda函数(必须部署在us-east-1区域) resource "aws_lambda_function" "security_headers_edge" { filename = "lambda.zip" function_name = "api-security-headers-edge" role = aws_iam_role.lambda_edge_role.arn handler = "index.handler" runtime = "nodejs18.x" region = "us-east-1" } # 关联Lambda到CloudFront的origin-response事件 resource "aws_cloudfront_distribution_association" "lambda_edge" { distribution_id = data.aws_cloudfront_distribution.api_gateway_dist.id event_type = "origin-response" lambda_arn = "${aws_lambda_function.security_headers_edge.arn}:${aws_lambda_function.security_headers_edge.version}" }
注意:需要为Lambda配置允许CloudFront调用的IAM权限。
可行性总结
- 方案1:完全可行,是长期维护的最优解,代价是需要切换到自定义域名;
- 方案2:可行,适合快速验证或API数量少的场景,但配置繁琐;
- 方案3:可行,无需更换域名,但需要管理Lambda@Edge的部署和权限。
内容的提问来源于stack exchange,提问作者Ahad Mammadov
相关产品推荐
相关产品推荐

