发布于20小时前20小时 背景在授权渗透测试中,后台接口经常同时依赖登录态和来源 IP 白名单。应用部署在 Nginx、Ingress 或云负载均衡之后时,开发人员通常从 X-Forwarded-For、X-Real-IP 获取客户端地址。如果代理链和可信代理范围配置错误,攻击者可自行注入请求头,将外部请求伪装成 127.0.0.1 或办公网地址,最终绕过应用层访问控制。本文以一条典型链路为例:客户端到公网 Nginx,再到应用服务。测试目标是验证来源地址是否可伪造、确认信任边界、检查日志是否保留原始链路,并给出可直接落地的修复配置。所有操作应仅在明确授权的目标及测试窗口内进行。问题定位先确认限制来自哪一层。常见现象是普通请求返回 403,而增加来源地址头后响应发生变化。不要只依据状态码判定漏洞,还要对比响应长度、跳转位置、页面特征和服务端审计日志。target=' # 基线请求 curl -sk -o /tmp/base.body -D /tmp/base.head -w 'base status=%{http_code} size=%{size_download} redirect=%{redirect_url}\n' "$target" # 分别测试常见客户端地址头,不要一次加入多个头 for h in 'X-Forwarded-For: 127.0.0.1' 'X-Real-IP: 127.0.0.1' 'Forwarded: for=127.0.0.1' do curl -sk -o /tmp/test.body -D /tmp/test.head -w "$h status=%{http_code} size=%{size_download} redirect=%{redirect_url}\n" -H "$h" "$target" done sha256sum /tmp/base.body /tmp/test.body若基线为 403,加入单个 X-Forwarded-For 后变为 200,只能说明该头可能影响了决策。下一步要排除缓存、WAF 分支、认证跳转和偶发响应。建议重复测试,并使用一个明显不在白名单内的地址作为负对照,例如 198.51.100.10。技术分析X-Forwarded-For 通常是逗号分隔的地址链。代理会把直接客户端地址追加到已有值后面,但不同框架可能取最左、最右或第一个满足条件的地址。如果公网入口没有覆盖外部传入的头,而应用无条件取最左侧值,攻击者就能控制授权判断。位置常见错误结果公网 Nginx使用 $proxy_add_x_forwarded_for 保留外部伪造值下游看到攻击者提供的首地址应用框架无条件 trust proxy 或信任全部网段req.ip 可由请求头控制业务代码字符串前缀判断内网地址172.16.0.10.example 等异常值可能误判日志系统只记录解析后的 client_ip难以还原直接连接来源关键原则是:只有由受控代理接收的请求,才允许解析转发地址头;公网边界必须丢弃或覆盖客户端提交的同名头。应用还应基于明确的 CIDR 解析地址,而不是 startsWith 或正则片段匹配。操作步骤第一步,建立基线。记录请求时间、测试源 IP、请求 ID、状态码和响应摘要,避免后续无法与服务端日志对应。第二步,测试单头影响。分别验证 X-Forwarded-For、X-Real-IP 和标准 Forwarded,不要同时设置,以便定位真正被消费的字段。第三步,验证地址链解析规则。以下测试值可判断应用取最左还是最右,但不应尝试访问测试范围之外的接口。# 正向与负向对照 curl -sk -H 'X-Forwarded-For: 127.0.0.1, 198.51.100.10' -w '%{http_code} %{size_download}\n' ' -o /tmp/a.body curl -sk -H 'X-Forwarded-For: 198.51.100.10, 127.0.0.1' -w '%{http_code} %{size_download}\n' ' -o /tmp/b.body cmp -s /tmp/a.body /tmp/b.body; echo "body_equal=$?"第四步,从代理和应用日志双向核对。Nginx 的 $remote_addr 是直接 TCP 对端;$realip_remote_addr 可在 realip 模块改写地址后保留原始值。两者必须同时记录,否则一旦 client_ip 被伪造,审计链会失真。log_format security escape=json '{"time":"$time_iso8601",' '"request_id":"$request_id",' '"peer_ip":"$realip_remote_addr",' '"client_ip":"$remote_addr",' '"xff":"$http_x_forwarded_for",' '"method":"$request_method",' '"uri":"$request_uri",' '"status":$status}'; access_log /var/log/nginx/security_access.log security;第五步,检查实际配置而非只看模板。Nginx 可用 nginx -T 展开全部 include;Kubernetes Ingress 还要检查控制器 ConfigMap、入口 Service 的 externalTrafficPolicy,以及云负载均衡是否额外写入地址头。nginx -T 2>&1 | grep -nE 'real_ip|set_real_ip_from|proxy_set_header|forwarded' # Kubernetes 场景 kubectl -n ingress-nginx get configmap ingress-nginx-controller -o yaml kubectl -n ingress-nginx get deploy ingress-nginx-controller -o yaml | grep -nE 'use-forwarded-headers|enable-real-ip|proxy-real-ip-cidr'安全配置示例若架构只有一个受控负载均衡,Nginx 应仅信任该负载均衡所在 CIDR,并明确解析规则。下面的 10.20.0.0/24 只是示例,生产环境必须替换为真实、最小化的代理网段。# 仅信任受控代理网段,禁止写成 0.0.0.0/0 set_real_ip_from 10.20.0.0/24; real_ip_header X-Forwarded-For; real_ip_recursive on; location / { # 传给应用的值由当前 Nginx 重新生成 proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $remote_addr; proxy_set_header X-Request-ID $request_id; proxy_pass }如果公网 Nginx 本身就是信任边界,覆盖 X-Forwarded-For 通常比追加更稳妥。若确实存在多级受控代理,则应逐跳列出代理网段,并通过测试确认递归解析方向。不要为了“兼容”而信任所有私网段,因为同一内网中的普通主机并不等于可信代理。应用侧也要限制代理跳数或代理地址。例如 Express 不应直接使用 app.set('trust proxy', true),可改为固定跳数或受控网段,并使用成熟 IP 库执行 CIDR 判断。const proxyaddr = require('proxy-addr'); const trustedProxy = proxyaddr.compile([ 'loopback', '10.20.0.0/24' ]); app.set('trust proxy', trustedProxy); // IP 白名单只作为附加控制,后台仍需强认证和权限校验风险判定与证据只有当伪造地址头能稳定改变权限结果,且请求确实来自非白名单测试源时,才应判定为访问控制绕过。报告中至少保留以下证据:基线请求与伪造头请求的完整时间、状态码、响应长度和关键响应差异。边界代理日志中的 peer_ip、client_ip、原始 X-Forwarded-For 和 request_id。触发问题的最小配置片段,以及真实代理拓扑和可信 CIDR。负对照结果,例如外部保留地址未获得权限、移除伪造头后恢复 403。受影响接口范围,以及是否还需要有效登录态、特定角色或二次认证。验证应以最小影响为原则。优先使用健康检查、只读后台页面或专门测试接口,不执行数据修改、账号创建或敏感信息批量导出。修复建议在最外层受控入口删除外部提交的 X-Forwarded-For、X-Real-IP、Forwarded,再按统一规则重建。仅信任明确的负载均衡和反向代理 CIDR,禁止 0.0.0.0/0、全部 RFC1918 网段或不受控容器网段。应用层按可信代理地址或固定跳数解析客户端 IP,统一 IPv4、IPv6 和 IPv4-mapped IPv6 的规范化逻辑。不要将来源 IP 作为唯一认证因素。后台接口应同时执行会话认证、角色授权,敏感操作增加二次确认。日志同时记录直接对端、解析后客户端地址、原始转发头、请求 ID 和认证主体,并控制日志访问权限。在 CI 或预发布环境加入回归用例:外部请求即使携带 127.0.0.1 或办公网地址,也必须保持 403。总结这类漏洞的核心不是某个请求头本身,而是代理链中的信任边界不清。有效的渗透验证需要完成三件事:用正负对照证明权限结果可被外部输入影响;结合代理与应用日志定位地址解析发生在哪一层;通过最小可信 CIDR、入口覆盖请求头、应用强认证和完整日志形成闭环。修复后应从公网入口、代理节点和应用直连路径分别回归,避免只改一层配置后仍可从旁路复现。 网络请求、日志与边界流量分析示意 服务器、云资产与权限边界梳理示意
19小时前19小时 这类问题的关键不是“是否读取 X-Forwarded-For”,而是每一跳是否只信任自己的上游。验证时建议把直连后端和经过代理两条链路分开测,否则很容易把 CDN/WAF 的改写行为误判成应用漏洞。 先记录后端实际看到的来源信息。Nginx 可临时增加一组访问日志: log_format iptrace '$remote_addr peer=$realip_remote_addr ' 'xff="$http_x_forwarded_for" ' 'xri="$http_x_real_ip" uri=$request_uri status=$status'; access_log /var/log/nginx/iptrace.log iptrace; 然后分别验证正常请求、伪造头以及绕过前置代理直连后端: curl -i curl -i -H 'X-Forwarded-For: 127.0.0.1' curl -i -H 'X-Real-IP: 10.0.0.10' curl -i --resolve example.test:443:BACKEND_IP \ -H 'X-Forwarded-For: 127.0.0.1' 如果后两种请求能改变白名单判断,通常存在两个问题之一:后端对任意来源启用了 forwarded-header 信任,或者后端端口可被外部直接访问。修复应同时覆盖网络层和解析层,不能只在业务代码里调整取 IP 的顺序。 以 Nginx 为例,只声明实际反代节点的地址段,并开启递归解析;不要写成 0.0.0.0/0: set_real_ip_from 10.20.0.0/16; # 仅填写真实 LB/Ingress 网段 set_real_ip_from 192.0.2.15/32; # 固定出口代理 real_ip_header X-Forwarded-For; real_ip_recursive on; location /admin/ { allow 10.30.0.0/16; deny all; proxy_pass } 最外层代理还应覆盖而不是透传客户端提交的来源头。若 Nginx 就是边界代理: proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $remote_addr; proxy_set_header Forwarded ""; 多级代理场景不要在每层都无条件覆盖:边界层清洗客户端头,后续受信代理再用 $proxy_add_x_forwarded_for 追加。与此同时,用安全组或防火墙限制应用端口只能由 LB/Ingress 访问,例如: iptables -A INPUT -p tcp --dport 8080 -s 10.20.0.0/16 -j ACCEPT iptables -A INPUT -p tcp --dport 8080 -j DROP 最后建议把回归条件写明确:伪造 X-Forwarded-For、X-Real-IP、标准 Forwarded 以及重复请求头都不能改变鉴权结果;直连后端必须网络不可达;经过受信代理时,日志中的客户端地址与代理链顺序应符合预期。IP 白名单最好只作为附加限制,管理接口仍保留独立身份认证,避免代理配置再次变化后直接暴露权限。
创建帐户或登录后发表意见