文档目录
代理信任中间件

简介

本技术文档围绕 DouPHP 的“代理信任中间件”展开,系统性解释其工作原理与代理信任机制,包括:

  • IP 地址验证与可信代理判定
  • 协议检查(HTTPS 检测)与请求转发安全
  • 标准代理头部字段的解析与验证(X-Forwarded-For、X-Forwarded-Proto 等)
  • 如何配置可信代理列表与 Host 白名单
  • HTTPS 重定向与协议检测的实现细节
  • 完整配置示例与安全最佳实践
  • 面向初学者的概念说明与面向高级开发者的自定义扩展指南

项目结构

DouPHP 将代理信任能力以“中间件 + 请求对象 + 初始化阶段注入配置”的方式实现:

  • 中间件层:TrustProxyMiddleware 负责在管道最前置读取配置并设置 Request 的可信代理名单。
  • 请求层:Request 提供 ip()、isSecure()、host() 等方法,结合可信代理名单决定是否采信 X-Forwarded-* 等头。
  • 初始化层:InitTrait 在构造审计服务之前加载 security.php 并设置可信代理与 Host 白名单,确保早期调用不会误信伪造头。
  • 路由层:各端 Resolver 将 trust_proxy 别名映射到 TrustProxyMiddleware,并在默认中间件栈中启用。
graph TB
A["客户端请求"] --> B["Nginx/负载均衡(反向代理)"]
B --> C["DouPHP 入口"]
C --> D["InitTrait::loadSecurityConfig()<br/>加载 security.php<br/>设置 Request 可信代理/Host"]
D --> E["中间件管道: SecurityHeaders -> TrustProxy -> Throttle -> ..."]
E --> F["控制器/业务逻辑"]
F --> G["Response 输出"]

核心组件

  • TrustProxyMiddleware:从配置读取 trusted_proxies,调用 Request::setTrustedProxies(),幂等保证即使 Init 顺序异常也能生效。
  • Request:封装客户端 IP、协议、主机名的获取逻辑;仅在 REMOTE_ADDR 命中可信代理时,才采信 X-Forwarded-For / X-Forwarded-Proto。
  • InitTrait:在最早阶段加载 security.php 并设置可信代理与 Host 白名单,避免在审计日志或鉴权前被伪造头污染。
  • 安全响应头中间件基类:根据 security.headers 下发基线安全头,HSTS 仅在 HTTPS 且开启时下发。

架构总览

下图展示了请求进入后,如何通过初始化阶段和中间件链完成代理信任配置与后续处理。

sequenceDiagram
participant Client as "客户端"
participant Proxy as "反向代理/负载均衡"
participant App as "DouPHP 应用"
participant Init as "InitTrait"
participant MW as "中间件管道"
participant Req as "Request"
Client->>Proxy : HTTP/HTTPS 请求
Proxy->>App : 转发请求(可能携带 X-Forwarded-*)
App->>Init : loadSecurityConfig()
Init-->>Req : setTrustedProxies()/setTrustedHosts()
App->>MW : 执行中间件链
MW->>Req : TrustProxyMiddleware 再次断言 trusted_proxies
Note over Req,Proxy : 仅当 REMOTE_ADDR 命中可信代理时,才采信 X-Forwarded-*
MW-->>App : 继续处理业务
App-->>Client : 返回响应(可带安全头/HSTS)

详细组件分析

TrustProxyMiddleware:代理信任中间件

  • 职责:在中间件管道最前置读取配置中的 trusted_proxies,并写入 Request。
  • 设计要点:
    • 幂等性:即便 Init 阶段已设置过,中间件再次断言,确保语义集中可读。
    • 安全性:默认不信任任何代理(trusted_proxies 为空),必须显式配置可信代理出口 IP/CIDR。
  • 影响范围:决定后续 Request::ip() 与 Request::isSecure() 是否采信 X-Forwarded-* 头。
flowchart TD
Start(["进入 TrustProxyMiddleware"]) --> ReadCfg["读取 config/security.php<br/>security.trusted_proxies"]
ReadCfg --> SetReq["调用 Request::setTrustedProxies()"]
SetReq --> Next["调用下一个中间件"]
Next --> End(["结束"])

Request:IP、协议与主机名可信判定

  • IP 获取(ip()):
    • 候选来源:X-Forwarded-For(逗号分隔,左到右)、HTTP_CLIENT_IP、REMOTE_ADDR。
    • 可信判定:仅当 REMOTE_ADDR 命中 trusted_proxies(精确 IP 或 CIDR)时,才展开 X-Forwarded-*;否则仅使用 REMOTE_ADDR。
    • IPv4/IPv6 归一化:优先返回 IPv4(含 ::ffff:x.x.x.x 映射),其次 IPv6,兜底首段原始字符串。
  • 协议检测(isSecure()):
    • 优先判断 SERVER_PORT=443 或 HTTPS 非 off。
    • 若来自可信代理且存在 X-Forwarded-Proto,取最左侧值(多级代理场景),为 https 则视为 HTTPS。
  • 主机名(host()):
    • 支持 trusted_hosts 白名单校验,未命中时回落到白名单首项,防止 Host 头注入污染对外 URL。
flowchart TD
S(["Request::ip()"]) --> Collect["收集候选 IP<br/>X-Forwarded-For / HTTP_CLIENT_IP / REMOTE_ADDR"]
Collect --> Trusted{"REMOTE_ADDR 命中可信代理?"}
Trusted -- 否 --> UseRemote["仅使用 REMOTE_ADDR"]
Trusted -- 是 --> Expand["展开 X-Forwarded-For 序列"]
Expand --> Normalize["归一化 IPv4/IPv6"]
Normalize --> ReturnIP["返回首个有效 IP"]
UseRemote --> ReturnIP

初始化阶段:安全配置注入

  • 在构造 AuditService($request->ip()) 之前,先加载 security.php 并设置 Request 的可信代理与 Host 白名单,避免伪造头污染审计日志或鉴权流程。
  • 中间件层再次幂等断言,确保即使 Init 顺序异常也不致漏设。

安全响应头与 HSTS

  • AbstractSecurityHeadersMiddleware 根据 security.headers 下发基线安全头(X-Content-Type-Options、X-Frame-Options、Referrer-Policy、Permissions-Policy)。
  • HSTS 仅在 HTTPS 且配置开启时下发,避免在非 HTTPS 环境错误启用。

依赖关系分析

  • 中间件注册:
    • 后台端:AdminResolver 将 trust_proxy 别名映射到 TrustProxyMiddleware。
    • 前台端:FrontResolver 默认中间件栈包含 security_headers、trust_proxy、throttle,可选 user_auth、csrf。
  • 配置依赖:
    • security.php 提供 trusted_proxies、trusted_hosts、headers、session 等安全相关配置。
  • 运行时依赖:
    • Request 在初始化阶段被注入可信代理与 Host 白名单,后续所有 ip()/isSecure()/host() 均受其影响。
graph LR
Sec["security.php"] --> Init["InitTrait::loadSecurityConfig()"]
Init --> Req["Request::setTrustedProxies()/setTrustedHosts()"]
Admin["AdminResolver"] --> MW["中间件别名映射"]
Front["FrontResolver"] --> MW
MW --> TP["TrustProxyMiddleware"]
TP --> Req

性能考量

  • 可信代理匹配采用 CIDR 计算,复杂度与规则数量线性相关;建议合理划分网段,避免过多细粒度规则。
  • X-Forwarded-For 解析为简单字符串分割与 trim,开销极低。
  • 中间件链顺序:TrustProxy 置于限流之前,确保限流键基于可信 IP,减少误判与资源浪费。

故障排查指南

  • 症状:业务侧通过 request()->ip() 获取到的不是真实客户端 IP。
    • 排查点:确认 REMOTE_ADDR 是否为反向代理出口 IP;确认 security.trusted_proxies 是否包含该 IP/CIDR。
    • 参考:Request::collectClientIpCandidates() 与 isFromTrustedProxy()。
  • 症状:HTTPS 页面仍被识别为 http。
    • 排查点:确认 Nginx/负载均衡是否正确设置 X-Forwarded-Proto=https;确认 trusted_proxies 已包含代理出口 IP。
    • 参考:Request::isSecure()。
  • 症状:生成链接域名不正确或被篡改。
    • 排查点:配置 trusted_hosts 白名单,避免未命中的 Host 头污染;必要时回落至白名单首项。
    • 参考:Request::host() 与 trustedHosts()。
  • 症状:审计日志记录的攻击源 IP 不准确。
    • 排查点:确保 InitTrait::loadSecurityConfig() 在审计服务构造前执行;中间件层再次断言 trusted_proxies。
    • 参考:InitTrait::instantiateCommonFactories()。

结论

DouPHP 的代理信任机制通过“初始化阶段注入 + 中间件幂等断言 + 请求层严格校验”的组合,确保在反向代理环境下正确识别真实客户端 IP、协议与主机名,同时防范伪造 X-Forwarded-* 头的风险。默认“不信任任何代理”的安全基线,要求部署方明确配置可信代理出口,从而在可用性与安全性之间取得平衡。

附录:配置示例与安全最佳实践

配置可信代理与 Host 白名单

  • 在 config/security.php 中设置:
    • trusted_proxies:填写反向代理出口 IP 或网段(CIDR),例如内网网段。
    • trusted_hosts:填写站点规范域名与子域通配,防止 Host 头注入。
  • 三端中间件链已默认包含 trust_proxy,无需额外注册。

代理头部字段处理逻辑

  • X-Forwarded-For:
    • 仅在 REMOTE_ADDR 命中 trusted_proxies 时才会展开;按逗号拆分,从左到右取有效 IP。
    • 用于 ip() 方法,最终选择首个 IPv4(含映射)或首个 IPv6。
  • X-Forwarded-Proto:
    • 仅在 REMOTE_ADDR 命中 trusted_proxies 时才会采信;取最左侧值,为 https 则 isSecure() 返回 true。
  • HTTP_CLIENT_IP:
    • 作为备选来源之一,优先级低于 X-Forwarded-For,但仍需满足可信代理条件。

HTTPS 重定向与协议检测

  • 协议检测:
    • 优先依据 SERVER_PORT=443 或 HTTPS 非 off;若来自可信代理且 X-Forwarded-Proto=https,则视为 HTTPS。
  • HSTS:
    • 由安全响应头中间件在 HTTPS 且配置开启时下发 Strict-Transport-Security。
  • 重定向:
    • 业务侧可通过 redirect() 进行跳转;如需强制 HTTPS,可在网关/Nginx 层统一处理,或在应用层根据 isSecure() 做策略控制。

安全最佳实践

  • 最小信任原则:
    • 仅将反向代理出口 IP/CIDR 加入 trusted_proxies,避免宽泛网段导致信任面过大。
  • 白名单 Host:
    • 配置 trusted_hosts,防止 Host 头注入污染外链、邮件、缓存等。
  • 网关层防护:
    • 在 Nginx/负载均衡层清理不可信的头,只允许可信代理注入 X-Forwarded-*。
  • 审计与监控:
    • 关注审计日志中的 IP 来源,确保与 trusted_proxies 一致;对异常 IP 进行告警。
  • 渐进启用 HSTS:
    • 先在测试环境验证 HTTPS 链路,再在生产环境启用 HSTS,避免回滚困难。

初学者概念说明

  • 什么是代理信任?
    • 当网站部署在反向代理之后,服务器收到的直接连接来自代理,而非真实客户端。为了获得真实客户端信息,代理会添加 X-Forwarded-* 头。但这类头可能被恶意伪造,因此需要“信任名单”来限定哪些代理可以注入这些头。
  • 为什么重要?
    • 错误的 IP/协议/主机名会导致访问统计失真、限流失效、外链生成错误、会话劫持等安全问题。通过可信代理机制,可以在保障功能的同时降低安全风险。

高级开发者扩展指南

  • 自定义代理验证逻辑:
    • 可在 Request 中扩展 isFromTrustedProxy() 的判断逻辑,增加更多维度(如来源端口、TLS 指纹、证书校验等)。
    • 或通过中间件链追加自定义校验步骤,在 TrustProxy 之后进一步过滤可疑请求。
  • 多环境差异化配置:
    • 在不同环境(开发/测试/生产)维护不同的 security.php,严格控制 trusted_proxies 与 trusted_hosts。
  • 与限流/认证联动:
    • 确保 TrustProxy 位于限流之前,使限流键基于可信 IP;认证模块可结合 host() 与 scheme() 做更精细的策略控制。
添加日期:2026-10-05