跳转到帖子

咖啡馆实战:网站反向代理的真实 IP、权限边界与日志留痕排查

精选回复

发布于

背景

网站接入 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 鉴权,并保留足够的原始日志字段用于复核。

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

这类现场排查最容易踩的坑,是直接把客户端传入的 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,否则多台机器的记录很难准确对齐。

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

最近浏览 0

  • 没有会员查看此页面。