跳转到帖子

咖啡馆实战:网站接入 CDN 与 Nginx 加速后,如何验证真实 IP 信任边界和日志留痕

精选回复

发布于

背景网站为了抗流量、降低回源延迟,通常会在源站前部署 CDN、WAF 或自建反向代理。这个架构最容易被忽略的问题,不是缓存命中率,而是客户端真实 IP 的信任边界。如果源站无条件采用请求中的 X-Forwarded-For 或 X-Real-IP,攻击者就可能伪造来源地址,影响后台访问控制、接口限速、审计追踪和封禁策略。本文以已授权的网站与服务器测试为前提,说明如何检查代理链、验证配置、完善日志,并给出可落地的修复方案。典型场景某网站使用“CDN → Nginx → 应用服务”的链路。后台路径只允许办公网访问,应用根据 X-Forwarded-For 判断客户端 IP。测试时发现:源站公网端口可以直接访问,并且 Nginx 信任任意来源提交的 X-Forwarded-For。这类问题通常由两个配置缺口叠加产生:源站防火墙没有限制回源地址,攻击者可以绕过 CDN 直连源站。Nginx 的 realip 模块信任范围过大,或者应用直接解析用户可控请求头。问题定位先确认实际链路,不要只看域名解析结果。需要同时检查 DNS、监听端口、Nginx 配置和边界防火墙。# 查看域名解析及可能的 CDN CNAME
dig +short example.test
dig CNAME example.test

# 在源站检查监听地址和端口
ss -lntp

# 输出 Nginx 完整生效配置,重点搜索 real_ip 与 proxy_set_header
nginx -T 2>&1 | grep -nE 'set_real_ip_from|real_ip_header|real_ip_recursive|proxy_set_header|log_format'

# 检查主机防火墙规则
nft list ruleset
# 使用 firewalld 的环境也可执行
firewall-cmd --list-all如果源站对公网开放 80 或 443,还应在授权范围内使用源站地址进行回源验证。测试时通过 Host 指定目标虚拟主机,避免把结果误判为默认站点响应。# 仅在已授权目标上执行,将 192.0.2.10 替换为测试源站地址
curl -k -I --resolve example.test:443:192.0.2.10

# 检查服务端是否错误采信外部提交的真实 IP 头
curl -k --resolve example.test:443:192.0.2.10 \
-H 'X-Forwarded-For: 127.0.0.1' \
-H 'X-Real-IP: 127.0.0.1' \
的 remote_addr 默认表示与当前服务器建立 TCP 连接的对端地址。只有当这个对端是可信代理时,代理传入的真实 IP 头才有可信基础。set_real_ip_from 用来声明可信代理网段。若配置为 0.0.0.0/0,相当于允许任意客户端改写来源 IP。real_ip_recursive 开启后,Nginx 会沿地址链从右向左剥离可信代理,直到遇到第一个不可信地址。因此,可信代理列表必须精确,并随 CDN 官方回源网段变化及时更新。变量含义建议用途$remote_addrrealip 模块处理后的客户端地址应用限速与常规访问日志$realip_remote_addr改写前的 TCP 对端地址确认请求是否来自可信 CDN$http_x_forwarded_for原始请求头内容,可能由客户端影响仅用于取证留痕,不直接做鉴权$request_id请求关联标识串联代理与应用日志安全配置示例下面配置假设 CDN 回源网段为文档示例地址。生产环境必须替换为供应商正式公布的地址,并通过变更流程维护。http {
# 仅信任明确的 CDN/WAF 回源网段,禁止使用 0.0.0.0/0
set_real_ip_from 192.0.2.0/24;
set_real_ip_from 2001:db8:100::/48;
real_ip_header X-Forwarded-For;
real_ip_recursive on;

log_format security_json escape=json
'{"time":"$time_iso8601",'
'"request_id":"$request_id",'
'"client_ip":"$remote_addr",'
'"peer_ip":"$realip_remote_addr",'
'"xff":"$http_x_forwarded_for",'
'"host":"$host",'
'"method":"$request_method",'
'"uri":"$request_uri",'
'"status":$status,'
'"bytes":$body_bytes_sent,'
'"referer":"$http_referer",'
'"user_agent":"$http_user_agent",'
'"upstream":"$upstream_addr",'
'"upstream_status":"$upstream_status",'
'"request_time":$request_time,'
'"upstream_time":"$upstream_response_time"}';

access_log /var/log/nginx/security_access.json security_json;

server {
listen 443 ssl;
server_name example.test;

location / {
proxy_set_header Host $host;
proxy_set_header X-Request-ID $request_id;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_pass /> }
}
}配置修改后先进行语法检查,再平滑加载,避免因手工维护网段造成服务中断。nginx -t && nginx -s reload

# 观察加载后的错误日志
journalctl -u nginx --since '10 minutes ago' --no-pager
tail -n 100 /var/log/nginx/error.log源站边界加固只修正请求头解析还不够。更稳妥的方案是在网络层限制 80/443 只接受 CDN 或 WAF 回源地址,并将运维入口放入 VPN、堡垒机或专用管理网。# nftables 示例:执行前先确认远程管理通道,避免误封
nft add table inet webfilter
nft 'add chain inet webfilter input { type filter hook input priority 0; policy accept; }'

# 允许已建立连接
nft add rule inet webfilter input ct state established,related accept

# 允许示例 CDN 网段访问 HTTPS
nft add rule inet webfilter input ip saddr 192.0.2.0/24 tcp dport 443 accept
nft add rule inet webfilter input ip6 saddr 2001:db8:100::/48 tcp dport 443 accept

# 拒绝其他来源直连 HTTPS;上线前应通过维护窗口验证
nft add rule inet webfilter input tcp dport 443 counter drop云服务器还应同步检查安全组、负载均衡监听器和公网 IP 绑定。只在主机防火墙限制,而安全组仍全开放,会增加误配置恢复和旁路访问风险;反之,只依赖安全组也不利于主机侧形成完整控制与计数日志。日志排查方法验证重点是比较 peer_ip、client_ip 与 xff 三者关系。正常情况下,peer_ip 应属于可信 CDN 网段;client_ip 是剥离可信代理后的用户地址;xff 用于保留原始链路,但不能单独作为安全决策依据。# 查询 TCP 对端不属于预期回源网段的请求
jq -c 'select(.peer_ip | startswith("192.0.2.") | not)' \
/var/log/nginx/security_access.json

# 检查声称来自本机或内网的 X-Forwarded-For
jq -c 'select(.xff | test("(^|, ?)(127\\.|10\\.|192\\.168\\.|172\\.(1[6-9]|2[0-9]|3[01])\\.)"))' \
/var/log/nginx/security_access.json

# 按请求标识关联单次访问
jq -c 'select(.request_id == "待查询的请求ID")' \
/var/log/nginx/security_access.json若发现 peer_ip 是公网普通地址,而 xff 声称为 127.0.0.1、办公网或管理网地址,应优先排查源站旁路访问和 realip 信任范围。若 peer_ip 确实属于 CDN,但地址链异常,则需要确认 CDN 的请求头覆盖策略,避免其简单透传客户端已有的同名头。权限边界与应用层注意事项后台管理权限不要只依赖来源 IP。至少叠加强认证、独立身份鉴别和会话安全控制。应用应读取由受控反向代理覆盖后的标准字段,不要自行从 X-Forwarded-For 左侧取第一个地址。代理到应用的端口不应直接暴露公网。可绑定 127.0.0.1、Unix Socket 或内部专用网卡。缓存规则不要覆盖登录态、管理接口、验证码和包含敏感信息的响应。对 Authorization、Cookie 与 Set-Cookie 明确设置旁路策略。保留 CDN 边缘日志、Nginx 访问日志、应用认证日志和主机审计日志,并统一时间同步与 request_id。修复后的验证清单从非 CDN 地址直连源站 80/443,应被安全组或主机防火墙拒绝。通过正常域名访问,业务响应和缓存功能应保持正常。伪造 X-Forwarded-For 后,日志中的 client_ip 不应被任意改写。peer_ip 应稳定落在已登记的 CDN/WAF 回源网段。应用收到的 X-Real-IP、X-Forwarded-For 与 Nginx 解析结果一致。管理接口同时具备身份认证,不因伪造来源地址获得额外权限。日志轮转、留存周期、文件权限和集中采集状态均正常。风险与修复建议此类问题的风险通常不止是“日志 IP 不准确”。如果限速、后台白名单、风控规则或审计系统依赖错误的来源地址,可能进一步造成访问控制绕过、告警失真和事件追踪困难。推荐按优先级处理:先限制源站只能被可信代理访问;再收紧 set_real_ip_from;随后统一应用层取值方式;最后补齐结构化日志和异常检测。CDN 网段变更应使用受控脚本定期拉取、校验并生成配置,但不要未经审计直接覆盖生产配置。总结网站加速架构中的关键安全点,是明确每一跳的信任关系。TCP 对端是否可信、真实 IP 头由谁写入、源站能否被绕过、应用依据什么做权限判断,这四个问题必须一起验证。通过网络层限制回源、精确配置 realip、保留改写前后地址并关联应用日志,才能让加速能力、权限边界和日志取证同时成立。

网络流量与边界分析示意图
网络请求、日志与边界流量分析示意
服务器与云环境安全示意图
服务器、云资产与权限边界梳理示意

这类验证的重点不是“日志里能否看到客户端 IP”,而是确认只有 CDN 节点有权改写客户端 IP。建议分三组测试:正常经 CDN、伪造转发头、绕过 CDN 直连源站。

Nginx 侧应只信任 CDN 官方公布的出口网段,不要使用 set_real_ip_from 0.0.0.0/0。下面以 CDN 使用 X-Forwarded-For 为例;如果厂商提供专用头,应替换为对应字段:

set_real_ip_from 203.0.113.0/24;
set_real_ip_from 2001:db8:100::/48;

real_ip_header X-Forwarded-For;
real_ip_recursive on;

log_format edge_combined escape=json
  '{"time":"$time_iso8601",'
  '"request_id":"$request_id",'
  '"remote_addr":"$remote_addr",'
  '"peer_addr":"$realip_remote_addr",'
  '"xff":"$http_x_forwarded_for",'
  '"host":"$host",'
  '"request":"$request",'
  '"status":$status,'
  '"bytes":$body_bytes_sent,'
  '"ua":"$http_user_agent"}';

access_log /var/log/nginx/edge_access.log edge_combined;

这里的两个地址含义不同:$remote_addr 是经过 realip 模块处理后的客户端地址,$realip_remote_addr 保留实际连接到源站的对端地址,通常应为 CDN 节点。两者同时记录,后续才能判断是谁提交了转发头。

先验证正常链路:

curl -sS -D- 
tail -f /var/log/nginx/edge_access.log | jq .

正常情况下,日志中的 peer_addr 应落在 CDN 官方网段,remote_addr 才是访问端公网 IP。随后主动注入伪造头:

curl -sS  \
  -H 'X-Forwarded-For: 198.51.100.77, 192.0.2.88'

此时要结合厂商行为判断:有的 CDN 会覆盖该头,有的会在末尾追加。无论哪种,Nginx 都只能沿着“已信任代理链”向左解析,不能让用户自行提交的任意地址成为最终客户端 IP。建议同时检查原始 xff 和解析后的 remote_addr,不要只看应用层打印结果。

更关键的是验证源站是否能被绕过。先取得源站地址,再绕过 DNS 直接访问:

curl -vk --resolve example.com:443:ORIGIN_IP \
   \
  -H 'X-Forwarded-For: 198.51.100.77'

理想结果是连接被防火墙、安全组或源站 Nginx 拒绝。若仍能返回业务内容,即使 realip 配置正确,也存在绕过 CDN、绕过限速/WAF 的入口。源站入口应仅放行 CDN 网段;条件允许时再加 CDN 到源站的 mTLS 或带轮换机制的鉴权头,避免只依赖可伪造的普通请求头。

# 示例:先保留管理通道,实际执行前确认 CDN 网段完整
iptables -A INPUT -p tcp --dport 443 -s 203.0.113.0/24 -j ACCEPT
iptables -A INPUT -p tcp --dport 443 -s ADMIN_IP/32 -j ACCEPT
iptables -A INPUT -p tcp --dport 443 -j DROP

最后建议做两个持续性检查:一是定期同步并审计 CDN 网段变更,防止旧网段被回收后仍受信;二是对日志中 peer_addr 不属于 CDN 网段、转发链异常增长、同一请求 ID 在 CDN 与源站记录不一致的情况告警。日志轮转后也应做集中留存,并限制原始访问日志读取权限,因为转发头、查询参数和 Cookie 可能包含敏感信息。

创建帐户或登录后发表意见

最近浏览 0

  • 没有会员查看此页面。