发布于22小时前22小时 背景网站接入 CDN、WAF 或 Nginx 反向代理后,源站通常会从 X-Forwarded-For、X-Real-IP 等请求头恢复访客地址。这里如果没有限定可信代理范围,客户端就能自行伪造来源 IP,进而绕过后台 IP 白名单、污染审计日志,甚至触发错误的限流与封禁策略。下面以一套常见的网站架构为例:公网流量先进入 CDN,再由边缘节点访问 Nginx,最后转发到应用服务。验证应在明确授权范围内进行,测试请求使用独立账号和固定出口地址,避免影响正常用户。问题定位先确认“哪个组件决定真实 IP”,不要只看应用页面显示的地址。需要同时核对 CDN 回源头、Nginx realip 配置、应用框架代理设置以及访问日志格式。直接访问源站时,客户端能否自行提交 X-Forwarded-For。Nginx 是否对任意来源信任该请求头。应用是否无条件启用了 trust proxy 或同类配置。后台白名单判断使用的是连接地址,还是未经校验的请求头。日志中是否同时保留连接地址、解析后的地址和完整代理链。在授权测试窗口内,可以发送带唯一标记的请求,并对照代理层和应用层日志:curl -sk -o /dev/null -D - \ -H 'X-Forwarded-For: 127.0.0.1' \ -H 'X-Real-IP: 127.0.0.1' \ -H 'X-Audit-ID: cafe-realip-20250308-01' \ # 在 Nginx 节点按测试标记定位日志 grep 'cafe-realip-20250308-01' /var/log/nginx/access.log # systemd 管理的应用服务 journalctl -u webapp --since '10 minutes ago' --no-pager | \ grep 'cafe-realip-20250308-01'如果后台页面、应用日志或授权接口把来源识别成 127.0.0.1,而代理连接本身来自公网测试地址,说明信任边界存在问题。仅仅看到 X-Forwarded-For 出现在日志里不能直接判定漏洞,关键是它是否参与鉴权、白名单、限流或风控决策。技术分析Nginx 中最危险的模式,是对所有来源启用 real_ip_header,却把信任网段放得过宽。例如 set_real_ip_from 0.0.0.0/0 会让任意客户端控制 $remote_addr。若后台 location 又通过 allow 127.0.0.1、allow 10.0.0.0/8 限制访问,就可能形成权限边界绕过。代理链的正确处理原则是:只信任实际掌控的 CDN 回源网段或内网负载均衡地址;源站不得直接暴露给互联网;应用只信任固定跳数或明确代理地址;日志同时保存原始连接地址与解析结果。字段含义审计用途$remote_addrrealip 模块处理后的客户端地址业务限流与访问分析$realip_remote_addr建立 TCP 连接的原始对端地址确认请求是否来自可信代理$http_x_forwarded_for请求携带的完整转发链调查伪造、重复追加和代理链异常$request_id请求关联标识串联 CDN、Nginx 与应用日志操作步骤第一步,确认源站暴露面。除了域名解析结果,还应检查云防火墙、安全组和主机监听状态。源站 80、443 端口原则上只接受 CDN 或负载均衡网段。# 查看服务监听地址和进程 ss -lntp | grep -E ':(80|443|8080)\b' # 检查 Nginx 实际加载的配置,而不是只看单个配置文件 nginx -T 2>&1 | grep -nE \ 'real_ip_header|set_real_ip_from|real_ip_recursive|proxy_set_header|allow|deny|log_format' # 检查主机侧规则,云安全组仍需单独核对 nft list ruleset 第二步,核对可信代理配置。下面示例假设 192.0.2.0/24 和 2001:db8:100::/48 是已从服务商官方渠道确认并纳入变更管理的回源网段,实际部署不能照抄示例地址。http { 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 audit escape=json '{"time":"$time_iso8601",' '"request_id":"$request_id",' '"peer_ip":"$realip_remote_addr",' '"client_ip":"$remote_addr",' '"xff":"$http_x_forwarded_for",' '"host":"$host",' '"method":"$request_method",' '"uri":"$uri",' '"status":$status,' '"bytes":$body_bytes_sent,' '"ua":"$http_user_agent"}'; access_log /var/log/nginx/access_audit.log audit; 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_pass } }第三步,检查应用框架的二次信任。以 Express 为例,不建议无条件设置 app.set('trust proxy', true),因为不同入口路径的代理跳数可能不同。固定代理网段时可以使用显式信任函数:const proxyaddr = require('proxy-addr'); const trust = proxyaddr.compile([ 'loopback', '10.20.0.0/16', '192.0.2.0/24' ]); app.set('trust proxy', (ip) => trust(ip)); app.use((req, res, next) => { console.log(JSON.stringify({ requestId: req.get('X-Request-ID'), socketIp: req.socket.remoteAddress, clientIp: req.ip, proxyChain: req.ips, path: req.originalUrl })); next(); });第四步,复测正常路径和绕过路径。至少覆盖不携带转发头、伪造内网地址、多个地址组成的代理链、IPv6 地址以及直连源站五种情况。预期结果是:只有来自可信代理的连接才解析转发头;直连请求无法抵达源站,或其自带请求头不会改变鉴权所使用的客户端地址。日志留痕与取证修复时不要用覆盖后的 client_ip 替代原始 peer_ip,否则出现问题后无法判断请求究竟来自 CDN 节点还是源站直连。建议 CDN、Nginx 和应用统一传递请求 ID,并确保边界入口重建该值,避免客户端伪造关联标识。保留 peer_ip、client_ip、完整 X-Forwarded-For、请求 ID、主机名、方法、URI、状态码和用户代理。登录、权限变更和后台管理操作额外记录账号、会话标识摘要及操作结果,但不要记录密码、令牌和完整 Cookie。对 peer_ip 不在可信代理网段、X-Forwarded-For 出现私网或回环地址、代理链长度异常建立告警。日志文件使用独立权限,定期轮转,并发送到远端日志平台,降低主机失陷后被清理的风险。可以用 jq 快速筛选连接来源与解析结果不符合预期的记录:jq -c ' select(.peer_ip != null and .client_ip != null) | select( (.client_ip | startswith("127.")) or (.client_ip | startswith("10.")) or (.client_ip | startswith("192.168.")) ) | {time, request_id, peer_ip, client_ip, xff, uri, status} ' /var/log/nginx/access_audit.log风险与修复建议不要把来源 IP 作为后台访问的唯一鉴权条件。管理面应叠加身份认证、MFA、VPN 或零信任接入,并限制源站网络入口。set_real_ip_from 只配置已核验的代理网段,建立网段更新和回滚流程,避免长期使用 0.0.0.0/0。云安全组优先限制回源,主机防火墙作为第二层控制;同时检查 IPv6,避免只封 IPv4 后仍可直连。应用框架的代理信任必须与实际拓扑一致。存在多条入口链路时,按代理地址判断通常比固定跳数更稳妥。修改配置后先执行 nginx -t,再灰度加载;复测后台白名单、限流、登录审计和正常 CDN 流量。若此前已存在错误信任,应回溯后台接口与敏感操作日志,重点关注来源显示为回环、私网地址但 peer_ip 来自公网的记录。验证过程中不要尝试未授权的后台操作。证明可控请求头进入安全决策链,并展示无害接口上的访问差异,通常已经足够支撑风险结论。总结网站加速链路的问题往往不在单一配置项,而在 CDN、反向代理、应用和网络访问控制对“谁值得信任”的理解不一致。排查时应从连接对端开始,逐层确认真实 IP 的生成、传递、使用和记录方式。修复的重点是收紧可信代理范围、阻断源站直连、避免单独依赖 IP 鉴权,并保留足够的原始日志字段用于复核。 网络请求、日志与边界流量分析示意 服务器、云资产与权限边界梳理示意
20小时前20小时 这类现场排查最容易踩的坑,是直接把客户端传入的 X-Forwarded-For 当成真实 IP。正确做法是只信任明确的上游代理地址,并在代理入口覆盖旧请求头。 如果链路是“客户端 → Nginx 反代 → 应用”,入口代理建议覆盖,而不是追加客户端自带值: location / { proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $remote_addr; proxy_set_header X-Forwarded-Proto $scheme; proxy_pass } 如果前面还有 CDN、负载均衡或另一层 Nginx,则在后端只信任这些代理的固定网段: set_real_ip_from 10.20.0.0/16; set_real_ip_from 192.0.2.10/32; real_ip_header X-Forwarded-For; real_ip_recursive on; log_format trace '$time_iso8601 request_id=$request_id ' 'peer=$realip_remote_addr client=$remote_addr ' 'xff="$http_x_forwarded_for" ' 'host="$host" request="$request" status=$status ' 'upstream=$upstream_addr rt=$request_time urt=$upstream_response_time'; access_log /var/log/nginx/access_trace.log trace; 这里同时记录 $realip_remote_addr 和 $remote_addr 很重要:前者保留实际 TCP 对端,后者是经过 realip 模块解析后的客户端地址。只记解析结果,会丢掉判断伪造请求头所需的证据。 可以直接从非可信节点构造请求验证是否存在伪造: curl -k -H 'X-Forwarded-For: 8.8.8.8, 1.1.1.1' \ -H 'X-Real-IP: 9.9.9.9' \ nginx -T | grep -nE 'set_real_ip_from|real_ip_header|real_ip_recursive|proxy_set_header' ss -lntp journalctl -u nginx --since '30 min ago' 权限边界也建议一起确认:应用端口只监听回环或内网地址,不能因为有反代就默认后端不可直连;管理接口则单独做网络 ACL 和应用鉴权。 # 应用优先监听 127.0.0.1:8080 # 检查是否意外暴露在所有网卡 ss -lntp | grep ':8080' # 日志目录权限 namei -l /var/log/nginx/access_trace.log stat -c '%U:%G %a %n' /var/log/nginx /var/log/nginx/access_trace.log 留痕方面,除了访问日志,建议把代理配置变更、服务重载和日志轮转一并纳入时间线。日志里至少保留请求 ID、TCP 对端、解析后的客户端 IP、完整 XFF、Host、状态码和上游地址;敏感的 Cookie、Authorization、请求体不要直接落盘。最后还要核对 NTP、时区和 logrotate,否则多台机器的记录很难准确对齐。
创建帐户或登录后发表意见