发布于17小时前17小时 背景网站迁移到 Nginx 反向代理后,常见架构是公网 Nginx 负责 TLS、访问控制和静态资源,后端应用监听 127.0.0.1 或内网地址。一次授权测试中,管理接口 /admin/ 已配置统一鉴权,但测试发现访问 /api/admin/、大小写变体或经特定代理头转发后,仍可能绕过入口限制。这类问题通常不是应用本身存在认证漏洞,而是 Nginx 的 location 匹配、URI 重写、可信代理配置与后端路由边界不一致。它尤其适合在网站建设和服务器运维阶段提前检查。以下操作应仅用于自有网站或明确授权的测试环境。验证目标是确认权限边界,不应读取、修改真实业务数据。问题定位先明确请求经过的链路:CDN 或加速器、边缘 Nginx、站点 Nginx、应用网关、后端服务。不要只检查最终应用,因为鉴权可能位于链路中的任意一层。导出完整生效配置,而不是只看某个站点文件。确认所有监听端口,排除后端端口直接暴露。对比公网入口与本机回源请求的状态码、响应头和日志。检查 location 优先级、proxy_pass 是否携带 URI、rewrite 是否改变鉴权路径。确认 X-Forwarded-For、X-Real-IP 等头部只能由可信代理写入。技术分析下面是一类容易产生边界缺口的配置。管理员原本只希望保护 /admin/,但另一个通用 API 代理同样能够把请求送到包含管理路由的后端:location ^~ /admin/ { auth_request /_auth; proxy_pass } location /api/ { proxy_pass } location = /_auth { internal; proxy_pass }当客户端请求 /api/admin/users 时,Nginx 命中的是 /api/,并可能因 proxy_pass 末尾带斜杠而将 URI 替换为 /admin/users。后端看到的是管理路由,但入口层没有执行 auth_request。另一个常见误区是使用来源 IP 代替身份认证。如果站点直接信任客户端提交的转发头,攻击者可能伪造“内网来源”。真实客户端地址应由受信任的 CDN 或反向代理重写,公网请求中的同名头必须覆盖,而不是透传。操作步骤第一步是确认配置与监听面。以下命令适合在服务器本机执行:sudo nginx -T 2>&1 | less sudo nginx -T 2>&1 | grep -nE 'server_name|listen|location|proxy_pass|auth_request|real_ip' sudo ss -lntp sudo systemctl status nginx --no-pager # 检查后端端口是否被防火墙放行 sudo nft list ruleset # 使用 firewalld 的系统可改用: sudo firewall-cmd --list-all第二步是在不触碰业务数据的前提下,对同一路由做最小化差异验证。优先请求只读、无副作用或专门准备的探针路径:# 正常公网入口 curl -k -i curl -k -i # 本机模拟站点入口,避免直接访问后端业务接口 curl -i -H 'Host: example.test' curl -i -H 'Host: example.test' # 验证客户端伪造代理头是否会影响权限判断 curl -k -i \ -H 'X-Forwarded-For: 127.0.0.1' \ -H 'X-Real-IP: 127.0.0.1'判断时不要只看是否返回 200。还应比较 Location、WWW-Authenticate、响应体长度、后端标识和请求 ID。若 /admin/health 返回 401,而 /api/admin/health 返回后端健康信息,基本可以确认入口路由存在鉴权覆盖缺口。日志留痕与关联排查建议临时增加包含上游信息的日志格式,以区分请求在 Nginx 中命中了什么路径、被转发到哪个上游,以及应用最终返回什么状态。不要记录 Cookie、Authorization 或完整查询参数中的敏感值。log_format boundary '$time_iso8601 request_id=$request_id ' 'remote=$remote_addr realip=$realip_remote_addr ' 'host=$host request="$request" status=$status ' 'uri=$uri upstream=$upstream_addr ' 'upstream_status=$upstream_status rt=$request_time ' 'urt=$upstream_response_time'; access_log /var/log/nginx/boundary_access.log boundary;加载前先检查语法,再平滑重载:sudo nginx -t && sudo systemctl reload nginx sudo tail -F /var/log/nginx/boundary_access.log sudo journalctl -u nginx --since '30 minutes ago' --no-pager若应用支持请求 ID,应将 Nginx 生成的 ID传递给后端,方便跨层关联:proxy_set_header X-Request-ID $request_id; proxy_set_header Host $host; proxy_set_header X-Forwarded-Proto $scheme; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;修复配置核心原则是:管理接口只能通过单一、明确的入口到达;前端代理与后端应用都必须执行权限校验。不要依赖 URI 黑名单,也不要只依赖来源 IP。# 精确保护管理命名空间 location = /admin { return 308 /admin/; } location ^~ /admin/ { auth_request /_auth; proxy_pass proxy_set_header X-Request-ID $request_id; proxy_set_header Host $host; } # API 代理保留 /api/ 前缀,避免意外映射到后端 /admin/ location /api/ { proxy_pass proxy_set_header X-Request-ID $request_id; proxy_set_header Host $host; } location = /_auth { internal; proxy_pass proxy_pass_request_body off; proxy_set_header Content-Length ''; proxy_set_header X-Original-URI $request_uri; }这里将通用 API 的 proxy_pass 改为不带末尾斜杠,使后端继续收到 /api/...。但这只是路由层修正,后端仍应根据登录身份和角色校验管理接口,避免以后新增代理规则时再次暴露。如果前方存在 CDN,应只信任其官方回源地址段,并递归解析真实地址:# 地址段仅为文档示例,生产环境应替换为实际 CDN 官方网段 set_real_ip_from 192.0.2.0/24; real_ip_header X-Forwarded-For; real_ip_recursive on; # 防火墙同时限制 80/443 只接受 CDN 回源或运维出口, # 后端应用端口仅绑定回环或内网地址。回归验证测试项预期结果未登录访问 /admin/health401 或跳转统一登录页未登录访问 /api/admin/health404、403,且不能到达管理处理器伪造 X-Forwarded-For不会获得额外权限直接访问后端端口公网不可达合法管理员访问鉴权成功,日志含可关联请求 ID普通账号访问管理接口后端返回 403,而非仅依赖 Nginx风险与修复建议先封闭后端直连端口,再修复代理规则,避免入口修好但旁路仍然存在。在应用层实施角色权限校验,Nginx 鉴权只能作为第一道边界。将关键路由的未认证访问、连续 401/403、异常 URI 归一化结果纳入告警。配置变更应经过 nginx -t、灰度发布和回归用例,避免直接覆盖线上配置。保留变更前后的配置、测试时间、请求 ID和日志片段,便于复盘;敏感日志按最小权限保存。对 CDN 回源、真实 IP、TLS 终止位置建立文档,避免后续维护人员误信客户端头部。总结网站反向代理的权限问题,往往来自“入口看到的 URI”和“后端实际处理的 URI”不一致。排查时应把完整链路、有效配置、监听端口和跨层日志放在一起看。修复也不能停留在增加一条 Nginx 规则,而应同时收紧网络暴露面、统一代理语义,并在后端落实最终授权检查。 网络请求、日志与边界流量分析示意 服务器、云资产与权限边界梳理示意
16小时前16小时 这类绕过建议先排除“后端鉴权失效”,重点核对 Nginx 的 location 命中结果。实战里常见问题是:受保护目录配置了 auth_request,但正则 location、静态文件 location 或内部代理入口重新匹配后,没有继承鉴权。 可以临时打开 rewrite 调试日志,并在访问日志里记录原始 URI、规范化后的 URI、最终代理目标和鉴权状态: error_log /var/log/nginx/error.log notice; rewrite_log on; log_format auth_debug '$remote_addr "$request" status=$status ' 'request_uri="$request_uri" uri="$uri" ' 'args="$args" upstream="$upstream_addr" ' 'upstream_status="$upstream_status" ' 'auth_status="$auth_status" ' 'xff="$http_x_forwarded_for"'; access_log /var/log/nginx/auth_debug.log auth_debug; $request_uri 保留客户端原始请求,$uri 是 Nginx 规范化并可能经过 rewrite 的值。两者出现明显差异时,应检查重复斜杠、编码斜杠、..、尾部斜杠以及 rewrite 是否把请求送进了未鉴权的 location。可用下面的请求做差异对照,但只应在授权环境验证: for path in \ '/admin' \ '/admin/' \ '/admin//users' \ '/admin/../admin/users' \ '/admin%2fusers' do curl -sk -o /dev/null -D - " \ -H 'X-Request-ID: auth-check-001' done grep 'auth-check-001' /var/log/nginx/auth_debug.log 配置上建议把鉴权放在能够覆盖整个业务前缀的前缀 location 中,并使用 ^~ 阻止后续正则 location 抢占;鉴权子请求和内部代理入口则必须设为 internal: location = /_auth { internal; proxy_pass proxy_pass_request_body off; proxy_set_header Content-Length ""; proxy_set_header X-Original-URI $request_uri; proxy_set_header X-Original-Method $request_method; } location ^~ /admin/ { auth_request /_auth; auth_request_set $auth_status $upstream_status; proxy_set_header Host $host; proxy_set_header X-Original-URI $request_uri; proxy_set_header X-Forwarded-Proto $scheme; proxy_pass } location = /admin { return 308 /admin/; } 还要特别检查 proxy_pass 是否带 URI。下面两种写法转发语义不同,混用很容易造成前后端对“受保护路径”的理解不一致: # 保留 /admin/ 前缀 location /admin/ { proxy_pass } # 用 / 替换已匹配的 /admin/ 前缀 location /admin/ { proxy_pass } 修复后不要只验证返回码,还要确认未登录请求没有到达业务 upstream。可以同时观察 Nginx 日志和后端访问日志,并执行 nginx -T 查看合并后的完整配置,避免遗漏 include 文件中的正则 location: nginx -T 2>&1 | less nginx -t && nginx -s reload grep -E 'location|auth_request|proxy_pass|rewrite' \ /tmp/nginx-full.conf 边界上不建议仅靠 Nginx 做唯一授权层。反向代理负责统一入口鉴权,后端仍应校验用户身份和资源权限;同时覆盖 GET、POST、HEAD、OPTIONS,以及带参数、编码路径和尾斜杠的回归用例,才能避免配置调整后再次出现旁路。
创建帐户或登录后发表意见