发布于19小时前19小时 背景:这个现象是否算权限绕过?一个常见求助场景:后台通过 Nginx 限制办公出口 IP,正常从测试机访问返回 403;加上 X-Forwarded-For: 办公出口IP 后,却变成应用返回的 401。后台没有直接登录成功,这算不算漏洞?应该检查哪一层?结论:如果测试机原本不在允许范围内,且唯一变化是请求头,那么已经证明来源 IP 限制可能被绕过;但不能据此宣称后台身份认证也被绕过。下面按授权测试的方式拆开验证,不做口令尝试,也不访问业务数据。一、先明确链路和预期假设实际链路如下,地址均为示例,执行时替换为授权环境中的值:测试客户端 → 边缘代理 10.20.0.10 → 后台 Nginx → 应用 127.0.0.1:8080。办公出口:198.51.100.23,允许进入后台认证页面。非办公出口:应在后台 Nginx 被拒绝,不能到达应用。/admin/health 是已经确认无副作用的探测接口,未登录请求到达应用时固定返回 401。这里的 401 只是区分“进入应用认证层”和“在入口被拒绝”的观测信号。若环境返回登录跳转或统一错误页,需要结合日志判断,不能机械地用状态码下结论。二、问题定位:谁把请求头变成了客户端地址?首先检查实际生效配置,而不是只看某个站点文件。Nginx 的 include、上级配置继承、默认虚拟主机都可能影响结果:sudo nginx -T 2>&1 | grep -nE 'listen|server_name|include|real_ip_header|set_real_ip_from|real_ip_recursive|allow |deny |proxy_set_header' nginx -V 2>&1 | grep -- --with-http_realip_module完整的 nginx -T 输出可能包含内部地址、证书路径或配置中的凭据,不要直接贴到公开求助帖。定位时保留相关 server、location 和 http 级继承配置即可。重点找下面这种组合:# 危险示例:把所有连接来源都当成可信代理 set_real_ip_from 0.0.0.0/0; set_real_ip_from ::/0; real_ip_header X-Forwarded-For; real_ip_recursive on; location /admin/ { allow 198.51.100.23; deny all; proxy_pass }启用 realip 模块后,$remote_addr 可能被请求头解析结果替换;访问控制模块随后使用这个地址做判断。若可信代理范围设置成全网,外部请求中可控的地址就可能变成白名单判断依据。另一个容易漏掉的情况是:后台 Nginx 只信任了边缘代理,但边缘代理没有清洗外部传入的 X-Forwarded-For。信任代理地址正确,不等于代理转交的地址链可信。三、先补观测,再做最小验证在后台 Nginx 的 http 上下文临时增加独立日志格式,并在对应 server 中启用。不要记录 Cookie、Authorization 或完整请求体。# http 上下文 log_format ip_boundary escape=json '{"time":"$time_iso8601",' '"rid":"$request_id",' '"client":"$remote_addr",' '"peer":"$realip_remote_addr",' '"xff":"$http_x_forwarded_for",' '"uri":"$uri",' '"status":$status,' '"upstream_status":"$upstream_status"}'; # 对应 server 上下文 access_log /var/log/nginx/ip_boundary.log ip_boundary;$remote_addr 是 realip 处理后的地址,$realip_remote_addr 保留处理前的连接对端地址;在这条链路中,后者通常是边缘代理。$upstream_status 用于辅助判断请求是否进入应用,但发生重试或内部处理时仍需结合完整配置分析。配置检查通过后再重载,然后从不在白名单内的授权测试出口发送两次请求:sudo nginx -t && sudo systemctl reload nginx # 基线:预期由入口拒绝 curl -sS -o /dev/null -w 'baseline=%{http_code}\n' \ --connect-timeout 5 --max-time 10 \ # 只改变来源地址头,不带账号凭据 curl -sS -o /dev/null -w 'with_xff=%{http_code}\n' \ --connect-timeout 5 --max-time 10 \ -H 'X-Forwarded-For: 198.51.100.23' \ sudo tail -n 20 /var/log/nginx/ip_boundary.log有说服力的证据组合是:基线请求为 403,未出现上游响应;第二次请求的 client 变成办公出口地址,上游返回 401。此时可以描述为“来源地址访问控制绕过,应用认证仍生效”。仅凭页面内容变化,证据还不够完整。四、修复:逐跳建立地址信任边界下面配置只适用于“边缘代理直接接收客户端连接”的上述拓扑。如果边缘前还有 CDN、负载均衡或其他代理,需要先明确它们各自的地址来源和清洗规则,不能直接照搬。# 边缘代理:覆盖外部输入,而不是原样转交 # 前提:这里的 $remote_addr 就是真实客户端连接地址 location / { proxy_set_header Host $host; proxy_set_header X-Forwarded-For $remote_addr; proxy_set_header X-Real-IP $remote_addr; proxy_pass } # 后台 Nginx:只信任明确的上一跳 # 需要删除或收紧其他配置中继承的宽泛可信范围 set_real_ip_from 10.20.0.10/32; real_ip_header X-Forwarded-For; real_ip_recursive on; location /admin/ { allow 198.51.100.23; deny all; # 向应用传递已经解析、规范化的单个地址 proxy_set_header X-Forwarded-For $remote_addr; proxy_set_header X-Real-IP $remote_addr; proxy_set_header Host $host; proxy_pass }不要把 $proxy_add_x_forwarded_for 当成自动清洗机制:它会保留原有地址链再追加地址。多级代理确实可能需要保留链路,但每一跳都必须有明确的可信范围和解析方式。后台监听端口通过网络 ACL 或安全组只允许边缘代理连接,避免客户端绕开边缘直连。应用仅监听回环地址,或只允许后台 Nginx 访问;否则攻击者可能绕开 Nginx 的访问控制。应用框架中的 trusted proxies 也应限制为实际上一跳,不要启用“信任所有代理”。清点 Forwarded、X-Real-IP 等其他地址头,确保应用不会改用未经规范化的字段。必要时在代理层清除不用的头。来源 IP 只能作为附加控制,不能替代后台账号认证、角色校验和高风险操作的再次确认。五、修复后怎么验收?非白名单出口,不带地址头:仍为入口 403。非白名单出口,伪造办公出口地址:仍为入口 403,日志中的 client 不应变成伪造值。非白名单出口,传入逗号分隔的多地址链:仍不能影响最终白名单判断。办公出口,不带登录凭据:可以进入应用认证层,但不能取得已认证权限。在授权范围内检查后台直连路径:应被网络策略阻断,不能从其他入口访问同一应用。验收记录应保留测试时间、实际出口、请求差异、对应日志和生效配置版本。诊断日志设置保留期限和访问权限,避免长期积累客户端地址信息。总结:求助时给出哪些信息最有用?在问答中心提这类问题,建议提供脱敏链路图、相关 realip 与 allow/deny 配置、两次请求的差异,以及同时间段的 client、peer、xff、status、upstream_status。把“TCP 对端是谁”“谁声明了客户端地址”“哪一层执行授权”分开,通常就能定位。不要把 IP 白名单绕过扩大描述成完整登录绕过,也不要因为应用还有登录页就忽略入口权限边界失效。 网络请求、日志与边界流量分析示意 服务器、云资产与权限边界梳理示意
19小时前19小时 这个现象优先怀疑是某一层把客户端可控的 X-Forwarded-For 当成了 IP 白名单依据。不过先别只看状态码:403 变成 200,也可能只是返回登录页或统一错误页,需要确认是否真的拿到了受限接口的数据。 建议按下面顺序定位: 固定请求,只改一个头。保持 Cookie、路径、请求方法一致,先不要自动跟随重定向,保存响应头和响应体: URL=' curl -sS -D baseline.headers -o baseline.body "$URL" curl -sS -D modified.headers -o modified.body \ -H 'X-Forwarded-For: 已验证会改变结果的地址' "$URL" diff -u baseline.headers modified.headers sha256sum baseline.body modified.body 实际测试时带上相同的认证信息,注意别把凭证写进工单或共享日志。 找出是谁返回的 403。在入口代理临时记录连接对端、原始请求头、最终识别地址、上游状态和请求 ID,再关联 WAF、网关及应用日志。Nginx 开启 realip 模块时,可参考下面的日志格式,配置放在 http 块: log_format iptrace escape=json '{"rid":"$request_id","peer":"$realip_remote_addr",' '"effective":"$remote_addr","xff":"$http_x_forwarded_for",' '"status":"$status","upstream_status":"$upstream_status",' '"upstream":"$upstream_addr"}'; access_log /var/log/nginx/iptrace.log iptrace; 重点看:伪造后 effective 是否变化;403 时是否存在上游状态;应用的鉴权日志使用了哪个 IP。不要仅凭响应里的 Server 字段判断拦截层。 沿着命中的判断追代码或配置。查 Nginx 的 set_real_ip_from、real_ip_header、real_ip_recursive,以及应用的可信代理设置。尤其检查是否信任了 0.0.0.0/0,或者代码直接取 X-Forwarded-For.split(',')[0] 做后台放行。 修复的核心是明确代理信任边界,不是简单禁止这个头。例如,若 Nginx 就是直接面向客户端的最外层入口,且没有用 realip 把任意客户端头改写成 $remote_addr,转发给应用时可以覆盖而不是追加: location / { proxy_set_header X-Forwarded-For $remote_addr; proxy_set_header X-Real-IP $remote_addr; proxy_pass } 如果前面还有 CDN/LB,这段不能直接照搬:应只信任实际代理的出口地址范围,并按该代理的头部约定恢复客户端 IP。应用也只应信任来自指定代理的转发信息;后端端口通过防火墙或安全组限制为仅代理可达。同时检查是否还接受 Forwarded 等其他客户端地址来源,避免修了一个头、另一个仍可影响鉴权。 最后回归三类请求:不带头、伪造单个地址、伪造多级地址链,确认授权结果一致;再验证真实代理链下客户端地址仍能正确识别。后台权限本身应由登录身份和角色决定,IP 白名单只作为额外限制。
创建帐户或登录后发表意见