跳转到帖子

咖啡馆实战:反向代理与加速链路中的内网权限边界、日志留痕与修复

精选回复

发布于

本文以一个常见的网站部署场景为例:公网请求先经过 CDN 或负载均衡,再进入 Nginx 反向代理,最后转发到应用服务器。一次授权渗透测试中,问题并不在某个单独的高危漏洞,而是出现在代理信任边界、管理接口暴露和内网访问控制的组合配置上。通过低风险验证,可以确认攻击面、还原可能的内网访问链路,并给出可落地的修复和审计方案。一、背景:加速链路改变了原有的访问边界测试目标结构如下:公网域名由 CDN 或云负载均衡接入。边缘节点将请求转发到站点 Nginx。Nginx 将业务请求转发到应用服务器,应用服务器可以访问数据库、Redis 和内部管理服务。/admin、/actuator、监控面板等路径只允许办公网或运维网访问。实际排查时发现,站点同时存在两个边界问题:一是应用直接信任客户端提交的 X-Forwarded-For,二是 Nginx 对管理路径使用了不完整的来源地址限制。若 CDN、负载均衡、Nginx 和应用对客户端真实 IP 的理解不一致,就可能出现公网请求被误判为内网请求的情况。二、问题定位:先确认真实请求路径,再验证权限边界授权测试不应直接进行口令爆破或破坏性操作。第一步应通过响应头、访问日志和配置文件确认流量经过了哪些组件。检查响应头中的 Via、X-Cache、Server、X-Request-ID 等字段,判断是否存在 CDN 或负载均衡。在测试窗口内发送唯一的请求标识,随后在 CDN、Nginx 和应用日志中进行关联。分别从公网测试机和已获授权的内网跳板机访问同一路径,比较 HTTP 状态码、响应长度和日志中的来源地址。检查 Nginx 实际加载配置,而不是只查看某个站点配置文件。include 文件和容器挂载配置经常造成判断偏差。建议先执行以下只读命令,确认监听端口、Nginx 生效配置和近期请求记录:sudo nginx -T 2>/dev/null | grep -E 'listen|server_name|location|allow |deny |real_ip|proxy_set_header'
sudo ss -lntp
sudo journalctl -u nginx --since '2 hours ago' --no-pager
sudo tail -n 200 /var/log/nginx/access.log如果业务运行在容器中,还需要分别检查宿主机端口映射和容器内监听端口,避免把仅绑定在容器网络中的服务误判为公网暴露:docker ps --format 'table {{.Names}}\t{{.Ports}}'
docker inspect --format '{{json .NetworkSettings.Ports}}' web-app
docker exec web-app ss -lntp三、技术分析:真实 IP 头不能由客户端直接决定下面是一个容易出问题的配置示例:location /admin/ {
allow 10.10.0.0/16;
deny all;

proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $http_x_forwarded_for;
proxy_pass /> }这里的 $http_x_forwarded_for 直接取客户端提交的请求头。若后端应用再根据该字段判断来源地址,公网客户端就可能影响应用看到的 IP。即使 Nginx 本身的 $remote_addr 是公网地址,应用仍可能把请求头中的内网地址当成可信来源。较稳妥的做法是只信任已明确登记的 CDN 或负载均衡网段,并由 Nginx 重建真实来源地址:http {
log_format main_ext '$remote_addr realip=$realip_remote_addr '
'[$time_iso8601] $request '
'status=$status bytes=$body_bytes_sent '
'request_id=$request_id '
'upstream=$upstream_addr upstream_status=$upstream_status '
'xff=$http_x_forwarded_for';

# 这里只填写实际 CDN 或负载均衡出口网段,不能写成 0.0.0.0/0
set_real_ip_from 192.0.2.0/24;
set_real_ip_from 198.51.100.0/24;
real_ip_header X-Forwarded-For;
real_ip_recursive on;

access_log /var/log/nginx/access.log main_ext;

server {
listen 443 ssl;
server_name app.example.test;

location /admin/ {
allow 10.10.0.0/16;
allow 10.20.0.0/16;
deny all;

proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Request-ID $request_id;
proxy_pass /> }
}
}其中,set_real_ip_from 必须限定为实际的可信代理地址段;如果把所有来源都设置为可信代理,真实 IP 保护等于失效。real_ip_recursive on 也不是越开越安全,必须结合可信代理网段使用,否则代理链中的伪造地址仍可能影响解析结果。四、操作步骤:用低风险方式验证管理接口和内网链路1. 验证管理路径的来源限制在授权范围内,对同一 URL 分别使用正常请求和测试用来源头进行对比。验证目标只选择页面首页、健康检查或明确允许的只读接口,不提交表单,不修改配置。curl -sk -D /tmp/normal.hdr -o /tmp/normal.body \

curl -sk -D /tmp/test.hdr -o /tmp/test.body \
-H 'X-Forwarded-For: 10.10.10.10' \
-H 'X-Real-IP: 10.10.10.10' \

printf 'normal: '; awk 'NR==1{print}' /tmp/normal.hdr
printf 'test: '; awk 'NR==1{print}' /tmp/test.hdr
wc -c /tmp/normal.body /tmp/test.body判断时不能只看返回码。需要同时核对 Nginx 日志中的 remote_addr、realip_remote_addr、xff 和应用日志中的鉴权结果。如果公网请求在应用侧被记录成内网地址,说明信任链存在缺陷;如果 Nginx 已经拒绝,但应用日志出现了请求,说明前面可能还有另一条绕过 Nginx 的入口,例如容器端口、旧负载均衡地址或备用域名。2. 核查应用服务器到内部服务的可达范围在获得明确授权的跳板机或测试代理上,针对资产清单中的少量端口做连接性验证。不要进行全网扫描,也不要尝试登录内部服务。for target in 10.10.20.15 10.10.20.16; do
for port in 8080 8443 6379 9100; do
timeout 3 bash -c

网络流量与边界分析示意图
网络请求、日志与边界流量分析示意
这个场景建议把重点放在“边界确认”和“证据链完整性”上,不要只看反代层是否挡住了外网访问。加速链路里一旦有 CDN / 反代 / 内网服务多层转发,最容易出问题的是信任头和源站直连。 可以按下面几个点排: 1. 确认源站是否可被绕过访问 如果源站公网 IP 暴露,反代上的鉴权、限速、WAF 都可能被绕过。可以在授权范围内直接测:
curl -k -H "Host: example.com" 
curl -k -H "Host: example.com" 
如果能直接返回业务页面,源站防火墙要改成只允许反代/CDN 回源 IP:
# 例:仅允许反代节点访问 80/443
iptables -A INPUT -p tcp -s 10.10.10.5 --dport 80 -j ACCEPT
iptables -A INPUT -p tcp --dport 80 -j DROP
iptables -A INPUT -p tcp -s 10.10.10.5 --dport 443 -j ACCEPT
iptables -A INPUT -p tcp --dport 443 -j DROP
云上就用安全组做同样的限制,别只靠 nginx 配置。 2. 反代不要盲信 X-Forwarded-For 咖啡馆/靶场这类“多层代理”环境里,经常能通过伪造 `X-Forwarded-For`、`X-Real-IP` 影响后台白名单、日志归因或限速逻辑。nginx 建议只信任上一跳代理 IP:
set_real_ip_from 10.10.10.5;
real_ip_header X-Forwarded-For;
real_ip_recursive on;
同时应用层不要直接取用户传入的 `X-Forwarded-For` 做权限判断。更稳的是在反代层覆盖这些头:
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
如果上游还有 CDN,只允许 CDN 过来的真实 IP 头,比如 `CF-Connecting-IP` 或厂商等价字段,并限制来源 IP 段。 3. 内网权限边界不要靠“内网地址默认可信” 很多问题不是反代本身,而是后端服务对 `127.0.0.1`、`10.0.0.0/8` 默认放权。重点查这些位置: - 后台管理路径是否只做 IP 白名单 - debug、actuator、metrics、swagger 是否对内网开放 - Redis、Mongo、Elasticsearch、Consul、etcd 是否仅绑定内网但无认证 - SSRF 点是否可以访问内网管理接口 可以在服务侧先自查监听面:
ss -lntup
ip addr
docker ps --format 'table {{.Names}}\t{{.Ports}}'
如果有 Docker,注意 `0.0.0.0:port->container` 这种映射,很多时候以为是内网服务,实际已经暴露到宿主机公网口。 4. 日志要保留“每一跳”的可关联字段 排查反代链路时,单看业务日志很难还原。建议 nginx access log 至少带上 request id、真实连接 IP、转发链、host、upstream:
log_format main_ext '$time_iso8601 '
                    'rid=$request_id '
                    'remote=$remote_addr '
                    'xff="$http_x_forwarded_for" '
                    'host="$host" '
                    'method=$request_method '
                    'uri="$request_uri" '
                    'status=$status '
                    'upstream=$upstream_addr '
                    'ustatus=$upstream_status '
                    'rt=$request_time '
                    'urt=$upstream_response_time '
                    'ua="$http_user_agent"';

access_log /var/log/nginx/access.log main_ext;

proxy_set_header X-Request-ID $request_id;
应用层也记录 `X-Request-ID`,这样可以从 CDN 日志、反代日志、应用日志串起来。否则后面做溯源只能靠时间窗口猜。 5. 修复顺序建议 我一般会按这个顺序处理: - 先封源站直连,只允许可信回源节点 - 清理/固定真实 IP 头,避免客户端伪造 - 内网管理接口加认证,不再依赖来源 IP - 关键服务最小化监听地址,能绑 `127.0.0.1` 就不要绑 `0.0.0.0` - 统一 request id 和日志格式 - 对敏感路径加独立审计和告警 最后可以做一个简单验证:从外网、反代节点、业务容器内分别访问管理路径和内网服务,记录哪些能通。这个比只看配置更可靠,尤其是有多层代理和容器网络时,实际连通性经常和预期不一致。
补一个容易被忽略的点:反代链路里不要只修“能不能访问”,还要确认后端服务有没有把代理传来的身份信息当成可信输入。 尤其是咖啡馆这种现场网络,常见问题是应用侧直接使用这些头做权限判断或日志归因:
X-Forwarded-For
X-Real-IP
X-Forwarded-Proto
X-Forwarded-Host
X-Original-URL
X-Rewrite-URL
Forwarded
如果后端没有限制“只信任来自反代的头”,攻击者走源站或内网入口时就可能伪造来源 IP、协议、Host,导致限速、后台 IP 白名单、审计日志都失真。 可以做两个验证:
curl -k  \
  -H "X-Forwarded-For: 127.0.0.1" \
  -H "X-Real-IP: 127.0.0.1" \
  -H "X-Forwarded-Proto: https" \
  -H "X-Forwarded-Host: internal.example.com"
再看应用日志里记录的是不是伪造 IP,以及权限判断有没有变化。 如果有变化,说明应用层信任边界放错了。 Nginx 侧建议明确清洗和重写,不要原样透传客户端传来的代理头:
location / {
    proxy_set_header Host $host;

    # 不直接信任客户端传入的 X-Forwarded-*,由反代重新生成
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $remote_addr;
    proxy_set_header X-Forwarded-Proto $scheme;

    proxy_set_header X-Forwarded-Host $host;
    proxy_set_header X-Original-URI $request_uri;

    proxy_pass 
}
如果前面还有 CDN,要用 `real_ip` 只信任 CDN/反代网段,别全局信任:
set_real_ip_from 10.10.10.5;
set_real_ip_from 10.10.10.6;
real_ip_header X-Forwarded-For;
real_ip_recursive on;
同时建议在日志里加一个链路 ID,不然事后很难把 CDN、反代、后端三层日志串起来:
map $http_x_request_id $req_id {
    default $http_x_request_id;
    ""      $request_id;
}

proxy_set_header X-Request-ID $req_id;

log_format main_ext '$time_iso8601 req_id=$req_id '
                    'remote=$remote_addr realip=$realip_remote_addr '
                    'xff="$http_x_forwarded_for" host="$host" '
                    'method=$request_method uri="$request_uri" '
                    'status=$status upstream=$upstream_addr '
                    'urt=$upstream_response_time rt=$request_time';

access_log /var/log/nginx/access.log main_ext;
修复后回归时我一般会看三件事: 1. 客户端伪造 `X-Forwarded-For` 后,应用日志是否仍记录真实代理链路。 2. 直连源站时,是否完全被防火墙或安全组挡住。 3. 后端是否还有基于 `Host`、`X-Forwarded-Host`、`X-Original-URL` 的路由绕过或后台路径绕过。 这个场景下证据链很重要,日志最好保留原始请求头、反代生成的真实来源、上游地址和 request id。否则即使修好了,也很难证明之前的访问路径到底是“正常回源”还是“绕过反代”。

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

最近浏览 0

  • 没有会员查看此页面。