经过代理如何获取真实IP 及 laravel 中配置可信代理的原理

客户端直接访问后端服务器时,$_SERVER['REMOTE_ADDR'] ji真实客户端 IP:

经过代理如何获取真实IP 及 laravel 中为何配置可信代理

remote_addr 是无法伪造的:因为建立 TCP 连接需要三次握手,如果伪造了源 IP,无法建立 TCP 连接。

可如果经过代理

经过代理如何获取真实IP 及 laravel 中为何配置可信代理

$_SERVER['REMOTE_ADDR'] 是上一级代理的 IP。

真实 IP 通常在 headers 中的 X-Forwarded-ForX-Real-Ip 来获取:

X-Forwarded-For 是一个 HTTP 扩展头部。HTTP/1.1(RFC 2616)协议并没有对它的定义,它最开始是由 Squid 这个缓存代理软件引入,用来表示 HTTP 请求端真实 IP。如今它已经成为事实上的标准,被各大 HTTP 代理、负载均衡等转发服务广泛使用,并被写入 RFC 7239(Forwarded HTTP Extension)标准之中。

X-Real-IP,这又是一个自定义头部字段。X-Real-IP 通常被 HTTP 代理用来表示与它产生 TCP 连接的设备 IP,这个设备可能是其他代理,也可能是真正的请求端。需要注意的是,X-Real-IP 目前并不属于任何标准,代理和 Web 应用之间可以约定用任何自定义头来传递这个信息。

我们只讨论 X-Forwarded-For。图2 中的 proxy 代理到 server 需如下配置

location / {
    proxy_pass http://server;
    proxy_set_header Host $host;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}

经过代理如何获取真实IP 及 laravel 中为何配置可信代理

同理,如果再有多个代理:

经过代理如何获取真实IP 及 laravel 中为何配置可信代理

通过观察,请求头 X-Forwarded-For 的组成是:client,proxy1,proxy2..

第一个 ip 就是客户端 ip,PHP 中获取: $_SERVER['HTTP_X_FORWARDED_FOR'][0]

X-Forwarded-For 是可以伪造的,当 client 发送这样的请求时:

curl http://xx/ -H 'X-Forwarded-For: 123.123.123.123'

proxy1 再转发给 proxy2

location / {
    proxy_pass http://proxy2;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}

最终:

经过代理如何获取真实IP 及 laravel 中为何配置可信代理

甚至是不法字符串:

curl http://xx/ -H 'X-Forwarded-For: select 1 = 1'

所以取第一个不一定准确,如果没有检查 ip 的格式,还有安全隐患。可以用以下方法检验 ip:

$long = sprintf("%u", ip2long($ip));
$ip = $long ? $ip : '0.0.0.0';

我们可以这样配置面向客户端的 proxy1 的代理转发,把真实客户端 ip $remote_addr 直接传给 `X-Forwarded-For:

location / {
    proxy_pass http://proxy2;
    #proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header X-Forwarded-For $remote_addr;
}

这样就保证了第一个是真实地址。

假设第一个有可能伪造,便引入了可信代理的概念。假设中间代理是可信任的,即不可能伪造 X-Forwarded-For。如果 server 端获取的 X-Forwarded-For 头是:

"http_x_forwarded_for": "select 1 = 1,1.1.1,2.2.2.2,3.3.3.3,4.4.4.4"

配置可信代理:

protected $proxies = [
    '4.4.4.4',
    '3.3.3.3',
    '2.2.2.2',
];

由后向前依次排除,直到 1.1.1.1 不在可信列表中,就认为是真实客户端 IP。

我们还可以通过 nginx 配置 set_real_ip_from 使 remote_addr 为客户端真实地址,此配置项依赖 nginx 扩展模块 http_realip_module

具体方法是,先 nginx -V 检查 nginx 是否包含模块 http_realip_module,然后在 nginx.conf 的 http 上下文添加:

# 可以添加多条指令,支持 IPIP段
set_real_ip_from 172.10.0.1; 
set_real_ip_from 10.0.0.0/16;
real_ip_header X-Forwarded-For;

经此设置后,nginx 的变量 $remote_addr 将为客户端 IP,它的原理也是上文中的原理。

参考:www.runoob.com/w3cnote/http-x-forw...

本作品采用《CC 协议》,转载必须注明作者和本文链接
welcome come back
本帖由系统于 1年前 自动加精
《L03 构架 API 服务器》
你将学到如 RESTFul 设计风格、PostMan 的使用、OAuth 流程,JWT 概念及使用 和 API 开发相关的进阶知识。
《L02 从零构建论坛系统》
以构建论坛项目 LaraBBS 为线索,展开对 Laravel 框架的全面学习。应用程序架构思路贴近 Laravel 框架的设计哲学。
讨论数量: 1

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!
未填写
文章
96
粉丝
24
喜欢
159
收藏
357
排名:315
访问:3.0 万
私信
所有博文
社区赞助商