Laravel可信代理Trusted Proxies获取客户端IP的原理咨询
你的两个推断核心逻辑成立,但存在部分细节偏差,以下是$request->ip()方法的完整执行逻辑,以及多层代理场景下的处理规则:
Laravel的IP获取能力底层继承自Symfony HttpFoundation Request组件,执行步骤固定:
- 首先获取TCP连接层面的对端IP,也就是PHP全局变量里的
$_SERVER['REMOTE_ADDR'],这个值是TCP握手时由内核记录的,无法通过普通请求头伪造。 - 校验这个
REMOTE_ADDR是否存在于App\Http\Middleware\TrustProxies中配置的可信代理列表内:- 若不在可信列表:直接返回
REMOTE_ADDR作为客户端IP,不会解析任何代理携带的自定义请求头,这也是未配置可信代理时,所有请求都返回代理节点公网IP的原因。 - 若在可信列表:才会根据中间件里配置的可信请求头字段,解析代理转发时携带的IP信息。
- 若不在可信列表:直接返回
这里补充细节:框架不会默认固定读取X-Forwarded-For头,只有当你在TrustProxies的$headers配置项里声明信任X-Forwarded-For时才会读取该字段;部分代理服务会使用X-Real-IP、CF-Connecting-IP等自定义头传递真实IP,需要对应配置才会生效。
当请求经过多层代理转发时,X-Forwarded-For头的格式为逗号分隔的IP序列,顺序为:真实客户端IP, 第一层代理IP, 第二层代理IP, ... , 倒数第二层代理IP,最后一个和源站建立连接的代理IP不会出现在这个头里,对应的值就是REMOTE_ADDR。
框架的解析规则为:
- 从
X-Forwarded-For列表的最右侧(离源站最近的代理IP)开始向左遍历 - 遍历过程中跳过所有在可信代理列表内的IP
- 遇到的第一个不在可信列表内的IP,就会被判定为真实客户端IP
举个实际链路例子:用户真实IP:1.1.1.1 -> CDN节点IP:2.2.2.2 -> 高防节点IP:3.3.3.3 -> 源站服务器
此时源站拿到的REMOTE_ADDR为3.3.3.3,X-Forwarded-For值为1.1.1.1,2.2.2.2:
- 如果仅把3.3.3.3加入可信代理:遍历XFF列表时,从右往左第一个IP是2.2.2.2,不在可信列表内,会被判定为客户端IP,拿到的是CDN节点地址,结果错误。
- 如果把2.2.2.2、3.3.3.3都加入可信代理:遍历XFF列表时,先遇到2.2.2.2属于可信代理跳过,再遇到1.1.1.1不属于可信代理,判定为真实客户端IP,结果正确。
安全提示:除非源站已经通过安全组、防火墙规则限制了只有上层代理节点可以访问,否则不要将
0.0.0.0/0(全量IP)配置为可信代理,否则攻击者可以直接伪造X-Forwarded-For请求头,篡改框架获取到的客户端IP。
内容的提问来源于stack exchange,提问作者Halil Yıldırım

