私有S3桶+Cloudfront Signed Url架构下,如何限制仅门户域名访问?
解决CloudFront+私有S3场景下Referer策略不稳定的问题
核心原因
你遇到的时好时坏问题,本质是CloudFront缓存机制导致的:当请求命中CloudFront缓存时,请求不会转发到S3,此时S3的Referer策略完全不生效;只有未命中缓存的请求才会触发S3的校验,所以出现部分文件有效、部分无效的情况。
解决方案
1. 将Referer校验移到CloudFront层(推荐方案)
让所有请求(无论是否命中缓存)都先经过CloudFront的校验,彻底避免绕过S3策略的情况,有两种实现方式:
方式一:使用Lambda@Edge(Viewer Request触发器)
创建一个Viewer Request阶段的Lambda函数,在请求进入CloudFront时直接校验Referer头,不符合则返回403:
exports.handler = (event, context, callback) => { const request = event.Records[0].cf.request; const headers = request.headers; // 替换为你的门户域名列表 const allowedDomains = ['www.example.com', 'example.com']; const referer = headers.referer ? headers.referer[0].value : ''; let isAllowed = false; if (referer) { try { const refererHost = new URL(referer).hostname; isAllowed = allowedDomains.includes(refererHost); } catch (e) { // 无效Referer格式,直接拒绝 isAllowed = false; } } if (!isAllowed) { const response = { status: '403', statusDescription: 'Forbidden', body: { content: '访问被拒绝:无效来源', contentEncoding: 'UTF-8', contentType: 'text/html' } }; callback(null, response); } else { callback(null, request); } };
将该函数部署到CloudFront分发的Viewer Request触发器,所有请求都会先经过这个校验。
方式二:配置CloudFront Origin请求策略(配合S3策略)
如果仍需要S3层的校验,必须确保CloudFront会把Referer头传递给S3:
- 创建Origin请求策略,添加
Referer到转发的头部列表 - 将该策略关联到CloudFront的分发行为
- 同时调整CloudFront缓存策略,避免不同Referer的请求共享缓存(比如把Referer加入缓存键)
2. 修正S3 Bucket Policy的问题
你的现有策略存在两个关键错误:
- Resource字段格式错误:
"arn:aws:s3:::/*"需替换为你的Bucket全称,比如"arn:aws:s3:::your-bucket-name/*" - 缺少兜底的Deny策略:仅靠Allow策略无法覆盖所有场景,需显式拒绝不符合条件的请求
修正后的Bucket Policy示例:
{ "Version": "2012-10-17", "Statement": [ { "Sid": "AllowValidRefererViaCloudFront", "Effect": "Allow", // 建议替换为CloudFront的OAI,而非*,进一步限制访问来源 "Principal": {"AWS": "arn:aws:iam::cloudfront:user/CloudFront Origin Access Identity YOUR-OAI-ID"}, "Action": "s3:GetObject", "Resource": "arn:aws:s3:::your-bucket-name/*", "Condition": { "StringLike": { "aws:Referer": [ "https://www.example.com/*", "https://example.com/*" ] } } }, { "Sid": "DenyInvalidReferer", "Effect": "Deny", "Principal": "*", "Action": "s3:GetObject", "Resource": "arn:aws:s3:::your-bucket-name/*", "Condition": { "StringNotLike": { "aws:Referer": [ "https://www.example.com/*", "https://example.com/*" ] } } } ] }
3. 启用CloudFront Origin Access Identity(OAI)
必须确保只有CloudFront能直接访问S3,杜绝绕过CloudFront直接访问S3的可能:
- 在CloudFront分发中创建并关联OAI
- 在S3 Bucket Policy中仅允许该OAI的
GetObject权限 - 关闭S3 Bucket的所有公共访问设置
额外防护建议
- Referer头可被伪造,若需更强防护,可结合CloudFront Signed Cookies+Lambda@Edge校验门户的用户会话(比如验证登录状态Cookie)
- 确保门户使用HTTPS,避免Referer头被明文传输或篡改
- 测试时可临时禁用CloudFront缓存,验证策略逻辑是否正确,再逐步开启缓存
内容的提问来源于stack exchange,提问作者Mozzak
相关产品推荐
相关产品推荐

