服务器获取真实客户端 IP

57次阅读

共计 4289 个字符,预计需要花费 11 分钟才能阅读完成。

服务器获取真实客户端 IP
0x01 先查个问题
测试环境微信支付通道提示网络环境未能通过安全验证,请稍后再试,出现这种情况一般首要 想到可能是双方网络交互中微信方验参与我们出现不一致,翻了下手册确定是这类问题开始排查环节

可能获取真实 IP 方式错误

getenv(‘HTTP_CLIENT_IP’)
getenv(‘HTTP_X_FORWARDED_FOR’)
getenv(‘REMOTE_ADDR’)
filter_var($remote_ip, FILTER_VALIDATE_IP)
已经依次获取并过滤
固程序没有任何问题,往上发散

是否反向代理经过反向代理后,由于在客户端和 web 服务器之间增加了中间层,因此 web 服务器无法直接拿到客户端的 ip,只能通过 $remote_addr 变量拿到的将是反向代理服务器的 ip 地址,检查不存在此类问题,再往上,擅长网络工程的同学表示绝不认输

可能 NAT 分配出口 IP,或负载均衡服务分发出现异常

先拿到我本地内网外网 IP 方便之后问题排查
# 本机 IP
ifconfig | grep -A 1 “en” | grep broadcast | cut -d ” ” -f 2
# 外网 IP
curl –silent http://icanhazip.com

检查与 80 端口建立连接目标都有谁
netstat -tn|grep 80|akw ‘{print $5}’|awk -F ‘{print $1}’ | grep [本地 IP]

这里出现问题,竟然没有我的 `IP`,再以 `nginx $remote_addr` 拿到的 IP 作为参考,这是 `nginx` 最后一次握手的 `IP`,`$remote_addr = 10.168.0.0/16` 段
在 `nginx` 处打印 `$remote_addr`,并在 `server_name` 添加当前机器 `ip`,分别以负载均衡 IP 与本地 IP 做测试,最终确定问题出现在负载均衡服务器出现异常

0x02 LNMP 栈拿真实 IP
LNMP 栈内 PHP 所有获得到的 TCP 操作信息都是由前面 Nginx 通过 fastcgi 传递给它的,就比如 $_SERVER[‘REMOTE_ADDR’] 由 include fastcgi.conf; 引进,其等于 nginx 的 $remote_addr
Nginx 中的几个变量:

$remote_addr 代表客户端的 IP,但它的值不是由客户端提供的,而是服务端根据客户端的 ip 指定的,icanhazip 的原理也是这样,当你的浏览器访问某个网站时,假设中间没有任何代理,那么网站的 web 服务器就会把 remote_addr 设为你在公网暴露的 IP,如果你用了某个代理,那么你的浏览器会先访问这个代理,然后再由这个代理转发到网站,这样 web 服务器就会把 remote_addr 设为这台代理机器的 IP, 除非代理将你的 IP 附在请求 header 中一起转交给 web 服务器。

$proxy_add_x_forwarded_for $proxy_add_x_forwarded_for 变量包含客户端请求头中的 ”X-Forwarded-For”,与 $remote_addr 两部分,他们之间用逗号分开。X-Forwarded-For(简称 XFF),X-Forwarded-For 是一个 HTTP 扩展头部。RFC 2616 协议并没有对它的定义,它最开始是由 Squid 这个缓存代理软件引入,用来表示 HTTP 请求端真实 IP。如今它已经成为事实上的标准,被各大 HTTP 代理、负载均衡等转发服务广泛使用,并被写入 RFC 7239(Forwarded HTTP Extension` 标准之中。

$proxy_set_header
已在排查问题中说明,可设置代理后 header
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;

X-Real-IP 一般比如 X -Real-IP 这一个自定义头部字段,通常被 HTTP 代理用来表示与它产生 TCP 连接的设备 IP,这个设备可能是其他代理,也可能是真正的请求端,这个要看经过代理的层级次数或是是否始终将真实 IP 一路传下来。(牢记:任何客户端传上来的东西都是不可信的)

当多层代理或使用 CDN 时,如果代理服务器不把用户的真实 IP 传递下去,那么服务器将永远不可能获取到用户的真实 IP。
0x03 用户的真实 IP 从何而来
宽带供应商提供独立 IP 比如家里电信宽带上网,电信给分配了公网 ip,那么一个请求经过的 ip 路径如下:
192.168.0.1 (用户) –>
192.168.0.1/116.1.2.3 (路由器的局域网 ip 及路由器得到的电信公网 ip)–>
119.110.0.0/16 (负载均衡服务器)–>
10.168.0.0/32 (业务处理服务器)
这种情况下,119.147.19.234 会把得到的 116.1.2.3 附加到头信息中传给 10.168.0.0/32, 因此这种情况下,我们取得的用户 ip 则为:116.1.2.3。如果 119.110.0.0/16 没有把 116.1.2.3 附加到头信息中传给业务服务器,业务服务器就只能取上上一级 ip 地址
宽带供应商不能提供独立 IP
宽带提供商没有足够的公网 ip,分配的是个内网 ip,比如长宽等小的 isp。请求路径则可能如下:
192.168.0.1 (用户) –>
192.168.0.1/10.0.1.2(路由器的局域网 ip 及路由器得到的运营商内网 ip)–>
211.162.78.1(网络运营商长城宽带的公网 ip)
119.110.0.0/16 (负载均衡服务器)–>
10.168.0.0/32 (业务处理服务器)
这种情况下得到的用户 ip,就是 211.162.78.1。这种情况下,就可能出现一个 ip 对应有数十上百个用户的情况了
手机 2g 上网
网络提供商没法直接提供 ip 给单个用户终端,以中国移动 cmwap 上网为例,因此请求路径可能为:
手机(手机上没法查看到 ip)–>
10.0.0.172(cmwap 代理服务器 ip)–>
10.0.1.2(移动运营商内网 ip)–>
202.96.75.1(移动运营商的公网 ip)–>
119.110.0.0/16 (负载均衡服务器)–>
10.168.0.0/32 (业务处理服务器)
这种情况下得到的用户 ip,就是 202.96.75.1。2008 年的时候整个广东联通就三个手机上网的公网 ip,因此这种情况下,同一 ip 出现数十万用户也是正常的。
有几万或数十万员工的公司,这种也会出现来自同一 ip 的超多用户,可能达到几万人,但出口 IP 可能就那么几个。
0x04 NAT [Network Address Translation]
中文意思是网络地址转换,它允许一个整体机构以一个公用 IP 地址出现在 Internet 上。
NAT 在 OSI 参考模型的网络层 (第 3 层), 它是一种把内部私有网络地址(IP 地址)翻译成合法网络 IP 地址的技术。NAT 可以让那些使用私有地址的内部网络连接到 Internet 或其它 IP 网络上。NAT 路由器在将内部网络的数据包发送到公用网络时,在 IP 包的报头把私有地址转换成合法的 IP 地址。
RFC1918 规定了三块专有的地址,作为私有的内部组网使用

A 类:10.0.0.0 — 10.255.255.255 10.0.0.0/8
B 类:172.16.0.0 — 172.31.255.255 172.16.0.0/12
C 类:192.168.0.0 — 192.168.255.255 192.168.0.0/16

这三块私有地址本身是可路由的,只是公网上的路由器不会转发这三块私有地址的流量;当一个公司内部配置了这些私有地址后,内部的计算机在和外网通信时,公司的边界路由会通过 NAT 或者 PAT 技术,将内部的私有地址转换成外网 IP,外部看到的源地址是公司边界路由转换过的公网 IP 地址,这在某种意义上也增加了内部网络的安全性
<center>
<img src=”//i.qh13.cn/t/1544243390591.png” width=”348″/>
</center>
这个过程是通过 NAT 中的本地址与全局地址映射条目来实现的,所以事先要在 NAT 路由器上配置这样的映射条目。
<center><img src=”//i.qh13.cn/t/1544243579906.png” width=”250″/></center>
通过这种方式一个公网 IP 底下可以发私有的 IP 地址。
0x05 IPV6 来了?
<center>
<img src=”//i.qh13.cn/t/1544241538381.png” width=”557″/>
</center>
写这篇文章的时候看到有个推送,表示阿里全面应用 IPV6, 这件事的意义还挺重大的
<center>
<img src=”//i.qh13.cn/t/1544241839285.png” width=”265″/>
</center>
我们知道,一段 IPv4 标准的 IP 地址,一共由 4 X 8 = 32 位二进制数字组成,理论上存在 2^32 个 IP 地址。等于 4,294,967,296,42 亿多个 IPv4 的地址。
<center>
<img src=”//i.qh13.cn/t/1544242178427.png” width=”751″/>
</center>
参考世界互联网用户统计报告,全球现在大概有 4,208,571,287 人在上网,也就是说已经快到 ipv4 地址设计的最大 IP 数了
不过不用担心,前面提到的 NAT,让 IPv4 公网 IP 哪怕用完了也能凑合过。
<center>
<img src=”//i.qh13.cn/t/1544243741020.png” width=”540″/>
</center>
到了 IPv6,相比 IPv4 最大的提升,就是位数大大增加,变成了 8 个 4 位的十六进制数字。也就是说有 2^128 个 IPv6 地址。地球上的每粒沙子都分一个也管够 <center><img src=”//i.qh13.cn/t/1544244141371.png” width=”300″/></center>
存储 2^128 字节理论上什么概念呢,在当今的量子水平下,假设计算设备能够操作在原子一级,每公斤质量可存储大约 10 的 25 次方 bits,存储 2 的 128 次方的字节大约需要 272 trillion = 2720000 亿公斤。
最后,周末愉快,北京联通已经支持 ipv6 了, 我在望京测试,可以拿到 ipv6 地址

正文完
 0