一个你每天在用、却可能从未防过的盲区——CDN IP
先看关键判断
如果你正在处理CDN IP,先别急着照搬网上的参数。当网站接入CDN(内容分发网络)后,源站收到的HTTP请求不再直接来自用户的浏览器,而是来自CDN的边缘节点。为了记录真正的访客IP,代理规范定义了 X-Forwarded-For(简称XFF)请求头,CDN节点会在转发请求时自动附加该头部,格式如 X-Forwarded-For: 用户IP, CDN节点IP。源站应用(PHP、Python、Nginx等)据此获取真实IP。
问题在于:XFF头并非只有CDN才能写入。任何向源站发起HTTP请求的客户端,都可以在请求中自行添加任意值的XFF头。如果源站没有对XFF头的来源做任何校验,攻击者就可以直接向源站发送一个伪造的XFF头,让应用以为请求来自某个合法IP,从而绕过IP白名单、登录限制、API频率控制甚至安全审计——这就是“伪造XFF头”漏洞。
先搞清楚三个基础概念,否则后面看不懂
1. 什么是X-Forwarded-For
它是HTTP扩展头,标准格式为逗号分隔的IP列表,最左边是原始客户端IP,每个经过的代理在后面追加自己的IP。例如:X-Forwarded-For: 1.2.3.4, 192.168.1.1 表示请求先来自1.2.3.4,然后经过192.168.1.1这台代理。
2. 源站为什么依赖它
没有CDN时,服务器可以从TCP连接直接拿到访客IP(remote_addr)。有CDN后,所有请求的remote_addr都是CDN节点IP,必须靠XFF来判断真实用户。许多安全功能——比如封禁恶意IP的访问控制列表(ACL)、限制单个IP的请求频率、基于IP的地理位置拦截——都基于这个头部。
3. 漏洞的根源
CDN转发时会在现有XFF的基础上追加自己的IP,但不会清除客户端原有的伪造部分。如果攻击者直接向CDN发送一个带虚假XFF的请求,CDN只会在其后面追加节点IP,最终源站看到的XFF中“最左边”仍是攻击者伪造的虚假IP。更危险的是,如果攻击者绕过CDN,直接将包含伪造XFF的请求发送到源站的真实IP,源站就会完全被欺骗。
实际场景:三个常见但容易被忽视的风险
实际操作要点
- 绕过IP白名单:后台管理页面只允许公司出口IP访问,攻击者在请求中伪造该IP,直接命中源站就能进入后台。
- 劫持会话:部分老旧应用依赖IP作为Session绑定的一部分,伪造IP可导致会话劫持。
- 规避API频率限制:限制每个IP每分钟100次请求,攻击者每次请求都换一个伪造IP,轻松超过限制。
防御的核心原则:只相信可靠的来源
验证与回滚
解决方案不是禁用XFF,而是验证XFF的添加者是否为信任的CDN节点。简单说:只有来自CDN节点IP的请求,其XFF头才值得信任;所有来自其他IP的请求,要么拒绝服务,要么忽略其XFF头并直接使用remote_addr。
方案一:最硬核——在源站防火墙层面拒绝非CDN流量(推荐)
先看关键判断
这是最彻底的方法:让源站只接收CDN节点的请求。配置步骤:
- 从CDN服务商处获取所有出口节点IP段(通常在控制台可下载JSON文件,或通过API实时查询)。
- 在源站的iptables或云服务商安全组中,只放行这些IP段的80/443端口。
- 其他IP的全部拒绝。
优势:攻击者无法直接访问源站,伪造XFF的攻击面被彻底消除。
缺点:需要定期更新IP列表(CDN节点可能变化),且国内CDN节点IP段可能较大,仍需配合后续方案。
方案二:在Nginx层校验XFF头(适用于Linux运维)
我的处理经验
如果因业务需要必须允许部分直连流量(比如服务器运维、CDN回源检查),可以在Nginx中用map指令结合geo模块来校验。
以下是一段经过生产验证的配置(请根据实际CDN IP段替换):
geo $trusted_ip {
default 0;
# 这里填入你的CDN所有IP段,以Cloudflare为例
173.245.48.0/20 1;
103.21.244.0/22 1;
# ... 其他IP段
}
map $http_x_forwarded_for $real_ip {
# 如果请求来自信任IP,取XFF最左边
"~^([^,]+)" $1;
default $remote_addr;
}
server {
listen 80;
# 仅在信任IP时才使用XFF,否则直接取remote_addr
if ($trusted_ip = 1) {
set $real_ip $real_ip;
} else {
set $real_ip $remote_addr;
}
proxy_set_header X-Real-IP $real_ip;
}
原理:先判断请求来源IP是否属于CDN段,若是,则取XFF头中最左边的IP作为真实IP;若不是,则无视XFF,直接使用$remote_addr。这样即使攻击者向源站直接发送带伪造XFF的请求,源站也不会采纳。
相关阅读:此处可内链到“CDN IP常见问题”专题。
方案三:在应用代码中主动校验(PHP/Python/Java)
验证与回滚
如果你的应用直接读取$_SERVER['HTTP_X_FORWARDED_FOR']或request.META['HTTP_X_FORWARDED_FOR'],请改为以下安全模式:
- 先判断请求是否来自CDN节点(检查
$_SERVER['REMOTE_ADDR']是否在CDN IP段内)。 - 如果来自CDN,则用XFF最左边的IP;否则,用
REMOTE_ADDR。 - 永远不要信任任何非CDN来源的XFF头。
PHP 示例:
function getRealIp() {
$remoteAddr = $_SERVER['REMOTE_ADDR'] ?? '';
// CDN IP段列表(建议从配置文件或Redis读取)
$cdnIps = ['173.245.48.0/20', '103.21.244.0/22', /* ... */];
if (ipInRanges($remoteAddr, $cdnIps)) {
$xff = $_SERVER['HTTP_X_FORWARDED_FOR'] ?? '';
if ($xff) {
// 取最左边第一个IP
$ips = explode(',', $xff);
return trim($ips[0]);
}
}
return $remoteAddr;
}
补充参考:此处可内链到“CDN IP故障排查实例”。
方案四:使用全站HTTPS + 边缘函数(进阶)
容易忽略的细节
部分CDN(如Cloudflare、Akamai)支持在边缘节点通过Worker或Edge Function清除客户端传入的XFF头,只保留服务器端添加的真实信息。这相当于从源头杜绝伪造。例如,在Cloudflare Worker中:
addEventListener('fetch', event => {
event.respondWith(handleRequest(event.request))
})
async function handleRequest(request) {
// 删除客户端可能提交的X-Forwarded-For
const newHeaders = new Headers(request.headers)
newHeaders.delete('X-Forwarded-For')
// 然后让CDN自动填入真实IP(Cloudflare会自己加)
return fetch(request, { headers: newHeaders })
}
这种方法要求CDN有自定义脚本能力,且需要确认删除后CDN会自动生成正确的XFF。不适合所有平台。
方案五:使用更安全的Forwarded标准头(But 生态尚不完善)
验证与回滚
RFC 7239定义了Forwarded头,语法为Forwarded: for=客户端IP;proto=http;by=代理IP。相比XFF,它更严谨且不易伪造——因为主流代理会自动覆盖已有Forwarded头,而不是追加。但目前CDN和应用的普及度不高,作为补充可选。
延伸阅读:此处可内链到“CDN IP配置案例”相关文章。
验证你的防御是否生效
先看关键判断
配置完成后,务必做以下测试:
- 正向测试:通过CDN访问网站,确认源站日志中记录的IP为真实用户IP(不是CDN节点IP)。
- 伪造测试:使用curl直接向源站公网IP发送请求,并带上伪造的XFF头,检查源站应用是否采用了该伪造值。例如:
curl -H "X-Forwarded-For: 8.8.8.8" http://你的源站IP/,查看日志中的IP应为源站IP,而不是8.8.8.8。 - 绕过测试:尝试直接访问源站IP(不经过CDN),看请求是否正确被拒绝或忽略XFF。
常见误区与避坑指南
验证与回滚
- 误区一:在CDN控制台开启“获取真实IP”就等于安全。 错,那只是告诉CDN要加XFF头,但源站如果不验证,一样被伪造。
- 误区二:只限制源站80/443端口的来源IP就够了? 不够,还需在应用层验证XFF,因为防火墙只能控制TCP连接,无法阻止通过CDN转发的伪造XFF(CDN本身是合法的)。
- 误区三:用Nginx的realip模块就能自动解决。
ngx_http_realip_module需要同时配置set_real_ip_from指定信任的CDN IP段,否则默认不限制,依旧有风险。
总结:一个头部,牵动整个安全防线与CDN IP
我的处理经验
X-Forwarded-For 是CDN时代源站与客户端之间唯一的“真实身份”凭证。伪造它不需要高超技术,只需要一个curl -H命令。防御措施也并非复杂:归根结底,永远不要信任来自非信托代理的头部信息。选择上述方案中的一种或组合,配合严格的网络访问控制,就能将这个漏洞关在门外。
建议各位站长或运维同学,在开启CDN后务必检查一次源站对XFF的处理逻辑,不要等到日志里出现本不属于你的IP时再亡羊补牢。把这些步骤跑通后,CDN IP基本就能稳定落地。
延伸阅读
