源站 IP 暴露被直接打穿?这套 AWS 纵深防御方案你一定用得上
上周半夜接到一个紧急电话,客户的业务挂了。打开监控一看,源站 ALB 的 CPU 直接拉满,连接数爆表。但奇怪的是 CloudFront 那边的请求量完全正常,WAF 日志也干干净净。
我当时就猜到了——源站 IP 漏了,攻击者绕过了 CloudFront 直接打源站。后来确认就是这样,客户的源站域名之前在 DNS 里解析过公网 IP,虽然后来改成了 CloudFront 的 CNAME,但历史记录在 SecurityTrails 上一查就有,攻击者拿这个 IP 直接对着 ALB 开干,CloudFront 上的 WAF 规则再多也没用,因为流量根本没经过 CloudFront。
这事儿让我觉得有必要把源站防护这个话题好好聊一聊。很多人觉得 CDN 前面挂个 WAF 就安全了,其实这只是第一道门,源站本身还在互联网上裸奔呢。
你以为的安全,其实只防了一半
我见过太多团队的架构是这样的:CloudFront 挂 WAF,后面接一个公网 ALB,ALB 后面是 ECS/EKS。WAF 规则配了一堆,IP Rate Limiting、Geo Block、Bot Control 全上了,看起来铜墙铁壁。
但你想想,WAF 防的是什么?是经过 CloudFront 的流量。攻击者如果直接找到你源站的公网 IP 或域名,绕过 CloudFront 直接访问 ALB,你的 WAF 规则一条都匹配不上。
找源站 IP 的手段多得很,我随便列几个:
- DNS 历史记录——你以前解析过的 IP 全留着呢,SecurityTrails 这种工具一搜就有
- 证书透明度日志——你申请 SSL 证书的时候域名信息就公开了
- Shodan、Censys 这种全网扫描引擎,端口 Banner 证书一把梭
- 还有各种泄露场景,邮件头里带源站 IP 的、API 返回值里暴露内网信息的、甚至有把源站域名硬编码在前端 JS 里的……
有人可能会说,那我源站前面也挂一个 WAF 不就行了?CDN 一层、源站一层,双重保险。我以前也这么想过,后来发现根本行不通。
源站 WAF 最大的问题是分不清谁是谁。经过 CDN 转发过来的请求,TCP 源 IP 是 CDN 节点的地址,真实客户端 IP 在 X-Forwarded-For 里。而直连源站的请求,TCP 源 IP 就是攻击者自己的,X-Forwarded-For 可以随便编。这两种请求搅在一起,WAF 就抓瞎了——按 TCP 源 IP 限速吧,CDN 节点的 IP 就那么几个,正常用户的请求也全被限了;按 X-Forwarded-For 限速吧,直连请求伪造这个头部跟玩一样。结果就是误报漏报一大堆,运维成本翻倍,效果约等于零。
所以思路得变:别在应用层纠结了,直接在网络层或传输层把非 CDN 流量掐死。
两条路,看你走哪条
我根据实际项目经验,总结了两种落地方案:
| 你的场景 | 推荐方案 | 一句话概括 |
|---|---|---|
| 用了好几家 CDN,源站在 AWS | mTLS 双向认证 | 靠证书验明正身,谁有证谁进来 |
| 只用 CloudFront,源站在 AWS | VPC Origin | 源站从互联网上消失,谁都找不到 |
下面详细拆解。
先说简单的:VPC Origin,让源站隐身
如果你的架构只用 CloudFront 一家 CDN,那 VPC Origin 是最省心也最安全的方案,没有之一。
原理其实很直白:把源站放到 VPC 私有子网里,不给它公网 IP,不给它公网域名。CloudFront 通过 AWS 内部的 ENI(弹性网络接口)直接连到你的私有子网,流量走的是 AWS 内网,不经过互联网。攻击者连目标都发现不了,更别提攻击了。
传统架构下源站是有公网 IP 的:
Client → CloudFront → ALB (公网IP,暴露了) Attacker ─────────────→ ALB (绕过CDN直接打)VPC Origin 架构下源站没有公网 IP:
Client → CloudFront ──ENI──→ ALB (私有子网,无公网IP) Attacker ────────── ✗ ─────→ 不可达,根本找不到打个不太恰当的比方,传统架构相当于你家大门装了监控(WAF),但房子就在马路边上,谁都能看到门牌号。VPC Origin 相当于把你家搬到了一个没有门牌号的地下室,只有知道暗道的人(CloudFront)才能进来,外人连你住哪都不知道。
配置过程
前提条件有几个要注意的:
- VPC 得挂上 Internet Gateway,这是 AWS 的技术实现要求。别担心,这只是个标识,实际不会有互联网流量通过 IGW 到达私有子网,你的源站还是安全的
- 源站必须在私有子网里,不能有 NAT Gateway,不能有 Internet 路由
- 支持 ALB、NLB、EC2 实例
创建 VPC Origin:
aws cloudfront create-vpc-origin\--vpc-origin-endpoint-config\Name=my-vpc-origin,\Arn=arn:aws:elasticloadbalancing:us-east-1:123456789012:loadbalancer/app/my-private-alb/abc123,\HTTPSPort=443,\OriginProtocolPolicy=https-only配置安全组,限制只有 CloudFront 能进来:
方式一,用 CloudFront Managed Prefix List:
aws ec2 authorize-security-group-ingress\--group-id sg-origin-xxx\--ip-permissionsIpProtocol=tcp,FromPort=443,ToPort=443,PrefixListIds=[{PrefixListId=pl-3b927c52}]方式二,用 CloudFront 托管安全组,这个限制更严格,只允许 CloudFront 的 ENI 访问:
aws ec2 authorize-security-group-ingress\--group-id sg-origin-xxx\--ip-permissionsIpProtocol=tcp,FromPort=443,ToPort=443,UserIdGroupPairs=[{GroupId=sg-cloudfront-vpc-origins-xxx}]我个人更推荐方式二,限制粒度更细。
把 VPC Origin 关联到 Distribution就简单了,在 CloudFront 控制台的 Origin 配置里选你刚创建的 VPC Origin 资源就行。
踩坑提醒:
- gRPC 和 Lambda@Edge Origin 触发器不支持,如果你的业务用了这些得另想办法
- IPv6-only 子网不行
- NLB 做 VPC Origin 时不支持 TLS Listener,需要 TLS 终结的话得用 ALB
- 部署大概要等 15 分钟,别急
- 支持跨账户共享,通过 AWS RAM 实现,多团队协作时很方便
DDoS?不存在的
VPC Origin 最让我舒坦的一点就是——源站根本不在公网上,DDoS 从何谈起?所有面向互联网的流量 CloudFront 接着,CloudFront 自带 AWS Shield Standard,L3/L4 的基础防护就有了。你不用额外买 Shield Advanced,不用在源站做任何 DDoS 防护配置。省钱省心。
再说复杂的:mTLS,多 CDN 场景的刚需
如果你的业务同时用了 CloudFront + Cloudflare,或者还加了 Akamai,源站都在 AWS 的 ALB 后面——这在出海业务里太常见了,国内走一家 CDN,海外走 CloudFront,合规要求还得再加一家。这种多 CDN 架构下,VPC Origin 就用不了了(它只认 CloudFront),得靠 mTLS。
为什么不是其他方案?
我知道有人会想到这几种方案,我逐个说说为什么不行:
Security Group 白名单——限制源站只允许 CDN 的 IP 段访问。能用,但 CDN 的 IP 段动辄几百个,Security Group 有 IP 数量限制容易超。而且 CDN 的 IP 范围会变,你得盯着更新,运维负担不小。IP 理论上还能被伪造。
自定义 Header——CDN 回源时加个X-My-Secret: abc123,源站校验这个 Header。简单是简单,但 Header 可以被抓包截获,也可以被伪造,安全性太低。一旦泄露就完了。
Direct Connect 专线——安全倒是安全,但每个 CDN 厂商拉一条专线,成本高得吓人,开通周期还长,灵活性差。
mTLS 优势在哪?认证发生在 TLS 握手阶段,比应用层早得多。就像你进小区,自定义 Header 是到了单元门口才查门禁卡,mTLS 是在小区大门口就验身份证——连小区都进不来,更别说单元门了。
具体来说:
- 传输层就完成身份认证,应用层没法伪造
- 每个 CDN 用自己的客户端证书,源站 Trust Store 统一管理
- 证书轮换可以平滑做,不影响业务
- 想断某个 CDN 的访问?删掉对应的 CA 证书就行
架构长这样
┌─────────────────────────────────────────────┐ │ AWS Cloud │ ┌──────────┐ │ ┌───────────────┐ ┌──────────────┐ │ │CloudFront│──mTLS──▶│ ALB (mTLS) │─────▶│ EC2 / ECS │ │ │(CDN #1) │ │ │ Trust Store │ │ (源站) │ │ └──────────┘ │ └───────────────┘ └──────────────┘ │ ┌──────────┐ │ ▲ │ │Cloudflare│──mTLS──────────┘ │ │(CDN #2) │ │ │ └──────────┘ │ │ ┌──────────┐ │ │ │ Akamai │──mTLS──────────┘ │ │(CDN #3) │ │ │ └──────────┘ └─────────────────────────────────────────────┘三家 CDN 各自带自己的客户端证书回源,ALB 的 Trust Store 里存了三家的 CA 证书,有证的放行,没证的 TLS 握手阶段就拒了。
配置实操
以 CloudFront + ALB 为例走一遍完整流程,其他 CDN 原理一样,只是 CDN 侧证书出示的方式不同。
CloudFront 侧:让 CloudFront 出示客户端证书
几个前提条件别漏了:
- CloudFront 定价计划得是 Business、Premium 或 Pay As You Go,免费版不支持
- 客户端证书要存到 ACM,而且必须在 us-east-1(CloudFront 的老规矩)
- 证书的 EKU 属性必须是 TLS Client Authentication,不然 CloudFront 不认
CLI 配置:
aws cloudfront update-distribution\--idE1EXAMPLE\--distribution-config'{ "Origins": { "Items": [{ "Id": "my-alb-origin", "DomainName": "origin.example.com", "CustomOriginConfig": { "HTTPSPort": 443, "OriginProtocolPolicy": "https-only" }, "OriginMtlsConfig": { "ClientCertificateArn": "arn:aws:acm:us-east-1:123456789012:certificate/abc-123" } }] } }'注意几个点:同一个 Distribution 的不同 Origin 可以配不同的客户端证书,挺灵活的。但不兼容 VPC Origins、gRPC、WebSocket 和 Lambda@Edge Origin 触发器,用了这些功能的得另想办法。还有如果源站不要求客户端证书,CloudFront 不会主动出示,连接照常走,这个行为是合理的。
ALB 侧:验证客户端证书
ALB 的 mTLS 有两种模式。Passthrough 模式是 ALB 不验证证书,把证书链通过 Header 传给后端让后端自己验。Verify 模式是 ALB 用 Trust Store 直接验证,通过才放行。
我强烈建议用 Verify 模式。Passthrough 把验证逻辑推给后端应用,增加复杂度还容易出漏洞。能在基础设施层解决的事别推到应用层。
Verify 模式配置:
创建 Trust Store,上传所有授权 CDN 的 CA 证书:
aws elbv2 create-trust-store\--namecdn-trust-store\--ca-certificates-bundle-s3-bucket my-certs-bucket\--ca-certificates-bundle-s3-key ca-bundle.pem修改 ALB Listener 启用 mTLS:
aws elbv2 modify-listener\--listener-arn arn:aws:elasticloadbalancing:...\--mutual-authenticationMode=verify,TrustStoreArn=arn:aws:elasticloadbalancing:...:truststore/cdn-trust-storeALB 在 Verify 模式下会自动注入一些 Header,可以用于后端审计和细粒度授权:
| Header | 内容 |
|---|---|
| X-Amzn-Mtls-Clientcert-Serial-Number | 证书序列号 |
| X-Amzn-Mtls-Clientcert-Issuer | 颁发者 |
| X-Amzn-Mtls-Clientcert-Subject | 主题 |
| X-Amzn-Mtls-Clientcert-Validity | 有效期 |
| X-Amzn-Mtls-Clientcert-Leaf | 叶子证书 |
比如你想区分请求是从 CloudFront 来的还是 Cloudflare 来的,看 Issuer 就行。做审计日志的时候也方便。
多 CDN 证书管理——运维的大头
配完了只是开始,证书的日常管理才是真正的考验。
ALB Trust Store 支持在一个 CA bundle 文件里放多个 CA 证书,只要 CDN 出示的客户端证书是 bundle 中任一 CA 签发的,ALB 就放行。不同 CDN 的证书签发方式不一样:
| CDN | 签发方式 | CA 证书怎么拿 |
|---|---|---|
| CloudFront | 自建 Private CA 签发,导入 ACM | aws acm-pca get-certificate-authority-certificate |
| Cloudflare | Zone-level AOP 用自建 CA;Global AOP 用 Cloudflare 公共 CA | authenticated_origin_pull_ca.pem |
| Akamai | Akamai-signed 或 Third-party CA | Control Center → CDN → mTLS Origin Keystore |
证书轮换我推荐"双 CA 并存"策略:先把新 CA 加到 bundle 里更新 Trust Store,等所有 CDN 节点都切到新证书了,再把旧 CA 从 bundle 里移除。整个过程零中断,不用停业务。
撤销访问更简单,从 Trust Store 里移除对应 CDN 的 CA 证书,立刻阻断该 CDN 的回源连接。比如你跟某家 CDN 解约了,删掉它的 CA 证书就完事了。
mTLS 架构下的 DDoS 防护
mTLS 方案有个不得不面对的问题:源站 ALB 还是暴露在公网上的。虽然只有持合法证书的请求能通过 TLS 握手,但 L3/L4 层的网络攻击不需要完成 TLS 握手就能消耗资源。
L7 层的攻击(HTTP Flood、CC 之类的)mTLS 天然免疫——攻击者没有合法的客户端证书和私钥,TLS 握手都完不成,HTTP 请求根本发不出来。
但 L3/L4/L5 层(SYN Flood、UDP 反射、TLS 握手洪泛)需要 AWS Shield Advanced 来扛。Shield Advanced 在 AWS 网络边缘就把攻击流量清洗掉了,ALB 根本感知不到。一个月 $3,000,不便宜,但高价值业务该花还是得花。
两种方案到底怎么选
直接看对比:
| mTLS | VPC Origin | |
|---|---|---|
| 安全性 | 高,传输层认证 | 最高,网络层隔离 |
| 多 CDN | ✅ | ❌ 只认 CloudFront |
| 源站公网暴露 | 还是需要公网域名 | 完全没有 |
| DDoS 防护 | L7 天然免疫,L3/L4 要 Shield Advanced | 天然免疫,不需要额外防护 |
| 证书管理 | 要管 | 不需要 |
| 部署难度 | 中等 | 低 |
| 兼容性限制 | 不支持 VPC Origin/gRPC/WebSocket | 不支持 gRPC/Lambda@Edge |
| 成本 | mTLS 免费,Shield Advanced 可选 $3,000/月 | 无额外费用 |
我的建议是:能用 VPC Origin 就用 VPC Origin,配置简单安全性最高还不用管证书。只有确实需要多 CDN 的场景才上 mTLS,但证书轮换的运维成本你得提前有心理准备。
简单画个决策流程:
用了多家 CDN? ├── 是 → mTLS,没得选 └── 否 → 只用 CloudFront? ├── 是 → VPC Origin,最省心 └── 否 → 看看其他 CDN 有没有 mTLS 能力最后说两句
源站防护这事儿,核心就一句话:让 CDN 成为源站的唯一入口,在网络层或传输层把非法直连掐死。别在应用层折腾 WAF 规则了,越往底层做越靠谱。
多 CDN 用 mTLS,TLS 握手阶段就验身份,伪造不了。纯 CloudFront 用 VPC Origin,源站从互联网上消失,攻击者连地址都找不到。不管哪种,都比"双 WAF"或者 IP 白名单强太多。
安全防护这东西,永远是在和攻击者比谁更底层——你在应用层做规则匹配,人家在网络层绕过你;你在网络层封堵,他就更难搞了。把防线往下沉,才是正道。
