jinhailang / blog

技术博客:知其然,知其所以然
https://github.com/jinhailang/blog/issues
60 stars 6 forks source link

Nginx 获取客户端 IP 的几种方式 #36

Open jinhailang opened 6 years ago

jinhailang commented 6 years ago

Nginx 获取客户端 IP 的几种方式

Nginx 服务端很多时候需要知道请求客户端的真实 IP,但是实际请求客户端可能经过了很多层代理才到的服务端,而误将代理 IP 当作请求客户端的 IP 可能导致很严重的问题,比如在 IP 维度做请求限速或者请求统计时。

这种情况下,要怎样获取请求端真实 IP 呢?

获取 IP 的三种方式

这两种方式的原理其实是一样的,都是跟上下游代理之间约定某个特定字段,将请求 IP 放进去,然后层层传递下去。 X-Forwarded-For 一般包含了整个请求经过的所有对端IP,以逗号+空格分割,最前(左)端的 IP 就是第一个请求客户端的 IP,因为这种方式被广泛的使用,可以作为业界共识。 但是,很明显的是,这种约定是脆弱的,也不安全,客户端甚至可以在请求的时候自己指定 X-Forwarded-For 头,这样就相当于可以随意篡改自己的 IP 地址了。当然,一般我们真正关心的是请求进入公司内网时的 IP,所以,可以在内网最前面的代理上做过滤。

proxy_bind 允许代理指定发起请求的 IP,因此我们可以如下设置(Nginx 1.11.0 开始支持的):

proxy_bind $remote_addr  transparent;

注意,需要开启 root 权限 user root;。这样设置后,Nginx 后面的服务器看到的 IP($remote_addr) 不再是 Nginx 反向代理服务器主机的 IP,而是真正发起请求的主机 IP。这就是所谓的“透明代理”。实现原理是在Linux 2.6.24以后,socket 增加了一个选项IP_TRANSPARENT可以接受目的地址没有配置的数据包,也可以发送原地址不是本地地址的数据包。可以通过该特性实现4层以上的透明代理。,详细分析可以看这里

相对上面两种,这种方式相对更简便,也更安全(初看以为也能篡改 IP,其实是不行的,必须是发起请求的 IP 才行,原因自行思考一分钟吧)。

小结

因此,最好都设置透明代理,这样后端就不需要特别去关注怎么获取真实的请求 IP 地址,直接取 ngx.var.remote_addr 值就是了。关键这也是最安全的方式,没有被篡改的风险。

想起以前做爬虫的时候,最大的难题就是爬取次数太多,IP 被限制了,只能花钱去买代理,然后代理又被封了,让人很头大。现在看来,也许可以在请求的时候加上 X-Forwarded-For 头?有时间可以试一试,理论上应该有些效果。