反向代理后的客户端 IP:正确处理转发头

反向代理后的客户端 IP:正确处理转发头原创封面

先分清连接来源与转发信息

应用部署在反向代理之后时,网络连接的直接来源通常是代理,而访问者地址通过请求头传递。这两类信息具有不同可信度:连接来源由当前网络连接提供,请求头则可能由客户端、代理或前置网关填写。如果应用直接采用某个转发头的第一个地址,攻击者可能构造请求影响日志、限流或审计中的身份判断。解析之前必须先明确允许谁提供这类信息。

本文讨论地址识别及信任边界,不把地址当成个人身份。一个地址可能代表共享网络、移动出口或多名用户,也可能在一段时间后变化。对请求限流时,可以将可信地址作为一个信号,但账号、会话和其他上下文仍有独立作用。公开日志展示和长期审计还应考虑数据最小化,避免无限保存原始访问地址。

把实际代理链画成明确顺序

最简单的部署是访问者连接代理,代理再连接只监听本机的应用。如果前面还有内容分发服务、负载均衡或其他网关,代理看到的来源就可能是这些中间节点。先记录每一跳的连接地址、转发规则和可达范围。不能因为某段地址属于内网,就假定任何来自内网的请求都具有相同可信度,不同工作负载可能共享网络。

代理链还可能存在备用入口,例如客户端绕过分发服务直接访问源站。信任规则必须说明这条路径允许还是拒绝。如果允许直连,源站需要能区分直连访问者与已核验的前置代理,不能对所有请求无条件信任同一个来源头。对多层代理,每层追加或覆盖什么内容都应有文档,避免两个组件各自认为另一个已经完成校验。

检查代理的默认处理方式

Caddy 反向代理文档说明了常见转发头的默认行为,以及可信代理配置如何影响读取。应先了解当前代理怎样处理客户端原先提交的头,再决定应用读取什么字段。不要在未检查默认行为时增加一个自定义透传规则,把原本被规范化的信息重新替换成客户端可控值。一个看似简短的头部配置也可能改变整条信任链。

如果代理直接面向公网,通常由它根据实际连接生成供上游使用的转发信息。如果代理位于受控前置网关后面,则只对明确的网关范围启用相应信任配置,并核对地址列表更新方式。文档和配置还要匹配正在运行的版本,特别是解析顺序和严格模式的差异。配置校验通过只表示语法可接受,仍需要伪造头请求验证实际行为。

应用入口本身也要受到约束

仅在代理上正确处理请求头,不能保护一个仍能被公网直接访问的应用端口。外部请求如果绕过代理,应用可能照常相信伪造的转发头。原生部署可以让应用只监听回环地址,并由代理访问;跨机器部署则通过网络访问规则和受控来源限制入口。每种方式都需要验证外部无法通过未受控路径调用应用接口。

应用内部仍应有清楚的来源约定。通过受保护连接收到指定头,可以使用经过代理规范化的值;不符合约定的连接应按真实来源处理或拒绝,而不是继续搜索多个备用头直到找到一个看起来像地址的字符串。回退逻辑越模糊,越容易让攻击者选中某个被遗漏的字段。健康检查和内部任务也需要符合各自入口规则。

多地址字段不能随意截取

传统转发字段可能包含多个以逗号分隔的地址,顺序与代理追加行为有关。RFC 定义的标准转发字段还包含参数、引号、地址族及其他表示方式,并非简单逗号切分就能完整处理。应采用可靠解析器,并按照可信链边界选择来源。不存在适用于所有部署的无条件取第一项或最后一项规则。

解析后校验地址格式,处理空值、过长字段、重复字段以及不能识别的标记。不要把异常原文直接拼进日志或数据库键。地址的规范化也要一致,否则同一来源的不同表示可能落到不同限流桶中。对地址族映射和端口附带形式,需要使用明确库规则并测试样例,不能仅通过几个字符串替换声称已经覆盖所有合法输入。

让日志、限流与审计共享解析结果

如果访问日志采用代理解析出的来源,限流采用应用自己取出的另一字段,排查时就会看到不同身份。应在请求入口建立统一的可信来源解析,并将结果提供给后续功能。审计可同时保留受控的来源类别,例如直连或经已核验网关,以解释异常。字段名称应清楚表达含义,避免把代理地址误写成访问者地址。

共享解析结果不意味着所有模块都要保存同样的原始信息。限流可以使用带作用域的不可逆标识,公开统计可以采用聚合数据,而敏感审计遵循独立保留策略。哈希并不能让地址失去所有隐私风险,密钥管理与用途限制仍然重要。设计时应明确哪些记录需要关联,哪些不应跨业务拼接成长期追踪身份。

信任列表与源站规则同步维护

前置服务的地址范围可能变化,信任列表需要有可靠来源与更新过程。更新时同时核对源站访问控制,避免信任范围新增了节点,但防火墙仍阻断,或访问控制开放了新范围而代理尚未正确识别。把列表当作部署输入保存版本与更新时间,并对异常来源留出诊断方式。不要在故障时临时信任所有地址后忘记恢复。

一个更稳妥的过程是先在受控环境验证新范围,再分阶段更新,并比较正常请求与伪造头请求的解析结果。对于无法确认是否可信的来源,应采用保守处理而非自动继承所有声明。源站存在多个站点时,还要检查各站点是否共用全局信任配置,以及某个站点新增入口是否影响其他应用。共享代理配置的变更范围需要明确。

用伪造输入验证边界是否成立

测试至少包含正常公网请求、客户端自填转发头、经受控网关请求,以及尝试直接访问应用端口。预期应事先写明:无关客户端不能指定自己的可信地址,受控网关提供的信息按约定解释,未开放的直连路径不能访问。采用隔离日志或受保护诊断接口观察结果,不要为了方便测试把地址解析结果永久暴露到公开页面。

对多跳链增加异常值、超长字段和不同地址族样例,确认错误不会导致服务器异常或绕过限流。然后用同一批请求比较代理日志、应用审计和限流桶,验证它们指向同一可信来源。测试中使用文档地址表示伪造值,不包含真实用户数据。是否通过应由实际记录支持,不能仅因配置文件包含可信代理字段就宣称防伪造完成。

地址变化不应成为唯一封禁依据

共享出口下的多个用户可能使用同一地址,过严的统一封禁会影响正常访问。登录接口可以分别控制请求总量、账号失败次数和可信来源失败次数,避免把其中一个信号扩展成所有业务的禁止访问。地址识别正确只是限流基础,阈值、窗口及恢复策略还需结合业务需要设计。成功认证也不应随意清除其他用户的风险记录。

地址范围调整后,观察限流命中分布与异常反馈,特别关注所有请求突然落到代理地址这一情况。它意味着解析或信任配置可能失效,不能马上提高阈值掩盖问题。先恢复来源识别,再评估限流规则是否合理。若需要限制原始地址保存,保留足以判断来源类别和规则版本的信息,也能帮助定位这类配置变化。

用最小入口示例说明约定

单机直连代理的示例可以保持简单:应用监听回环地址,代理按照默认规则转发,外部用户只访问域名入口。引入前置网关后,再根据官方文档及实际版本添加精确的信任配置。不要把未知前置网络的规则写进通用模板。部署文档应记录代理版本、应用监听位置和外部访问控制,使以后迁移机器时能够重建相同边界。

下面配置展示直接面向公网的单机入口,测试请求故意添加伪造转发值。命令只能产生输入,结果仍需从受保护诊断记录读取并与预期比较。示例没有配置任意网关信任范围,也没有提供应用层的通用地址解析函数,因为该函数必须依赖实际网络约定。测试结束应移除临时诊断输出,并保存脱敏的验收结论。

实现片段

blog.example.com {
  reverse_proxy 127.0.0.1:3100
}

# 在终端发起边界测试,并在受保护日志中观察解析结果:
# curl --fail --max-time 10 https://blog.example.com/api/health
# curl --fail --max-time 10 -H 'X-Forwarded-For: 203.0.113.50' \
#   https://blog.example.com/api/health
# 应用服务配置:HOST=127.0.0.1 PORT=3100

一手参考资料

评论 · 0

还没有评论留下第一句经过思考的话。

游客评论需审核。注册后可直接公开,无需审核。

SHARE / 分享

分享这篇文章

WECHAT / 微信

用微信扫一扫

在手机微信中打开文章后,再从微信右上角分享给朋友或朋友圈。