天天看点

阿里云场景下获取用户真实 IP

获取用户的真实 ip,对于安全业务来说非常重要。

阿里云场景下一个http 请求一般为:

用户ip --> ddos 高防 ip ->slb ip

对于web服务器来说,主要是通过两种方式获取 ip

与服务器建立tcp连接的地址 remote address

通过 http header 的 x-forwarded-for 字段

对应的 php 变量如下

与服务器建立tcp连接的 ip

无法伪造,很合适作为用户真实ip

但是 http 请求经过七层代理后,就不是用户ip了,一般为slb ip

通过http header传递给服务端

可以伪造,有可能获取的数据不准确,还可能引发 xss,sql 注入等问题

x-forwarded-for :格式如下

x-forwarded-for: client, proxy1, proxy2

如果一个 http 请求到达服务器之前,经过了三个代理 proxy1、proxy2、proxy3,ip 分别为 ip1、ip2、ip3,用户真实 ip 为 ip0,服务端最终会收到以下信息:

x-forwarded-for: ip0, ip1, ip2

proxy3 直连服务器,它会给 xff 追加 ip2,表示它是在帮 proxy2 转发请求。列表中并没有 ip3,ip3 可以在服务端通过 remote_addr 字段获得。

如果用户篡改 x-forwarded-for

x-forwarded-for: ip100, ip0, ip1, ip2

我们怎么通过这两个字段获取用户真实 ip 呢?

如果 http 请求没有经过七层代理,直接读取 remote_addr 字段最好

如果 http 请求经过七层代理, 我们只能从 x-forwarded-for 获取,但是我们必须过滤用户伪造数据,获取真实的用户 ip

如上面例子 ip1, ip2是我们自己配置的 ip,可信任的ip,过滤掉可信任的代理ip,最后一个不可信任的ip就是用户ip。

要解决的问题如下

nginx的访问日志,记录用户真实ip,现在记录的是remote address。

php获取用户真实ip,如果我们获取x-forwarded-for第一个ip。容易被用户篡改。

调研过程中,发现ngx_http_realip_module 满足我们需求, 当获取了用户真实ip,会赋值给 nginx变量 $remote_addr 变量,那么 php的 $_server['remote_addr'] 也变成了用户真实ip。

与web服务器建议tcp连接的ip 存储在变量 $realip_remote_addr 中。

set_ip_from

设置信任 ip,就是我们自己的代理ip,可以设置多个

real_ip_header

设置请求头字段,表示从x-forwarded-for字段获取用户 ip

real_ip_recursive

off:如果remote address 能够匹配set_real_ip_from 的ip,用户 x-forwarded-for最后一个 ip 作为用户ip。

on: 如果remote address 能够匹配set_real_ip_from 的ip,用户 x-forwarded-for最后一个不可信任 ip 作为用户ip。

我们的配置如下,注意还没有上线

nginx 配置如下

php 获取用户ip

$_sever['remote_addr']

<a href="https://imququ.com/post/x-forwarded-for-header-in-http.html">https://imququ.com/post/x-forwarded-for-header-in-http.html</a>

<a href="http://nginx.org/en/docs/http/ngx_http_realip_module.html">http://nginx.org/en/docs/http/ngx_http_realip_module.html</a>

<a href="http://baike.baidu.com/item/iana%e4%bf%9d%e7%95%99%e5%9c%b0%e5%9d%80?fr=aladdin">http://baike.baidu.com/item/iana%e4%bf%9d%e7%95%99%e5%9c%b0%e5%9d%80?fr=aladdin</a>