网站使用 CDN 之后获取访客原 IP 的方法

网络上大多数教程给出的方法都是通过修改程序代码,直接取 X-Forwarded-For 等请求标头的值替换原来 REMOTE_ADDR 变量的值作为原 IP,这种做法有很大的安全隐患,我认为不值得提倡。

安全隐患

根据 RFC 3875 文件,REMOTE_ADDR 变量的值必须为发送请求到服务器的客户端的网络地址。在实践中 REMOTE_ADDR 变量的值往往由服务器直接设置为发送请求的客户端网络地址,而不是由客户端提供,因此其真实性有保证。

与 REMOTE_ADDR 变量不同,X-Forwarded-For 请求标头的值不是由服务器直接决定,而可以由客户端自行设置,实践中一般由代理服务器在转发请求时设置。因为 X-Forwarded-For 请求标头的值可能被客户端伪造,所以不应该轻易相信其真实性。

由于 REMOTE_ADDR 变量的值和 X-Forwarded-For 请求标头的值具有不同的可信度,使用低可信度 X-Forwarded-For 请求标头的值去替换高可信度 REMOTE_ADDR 变量的值便会产生不可忽视的安全隐患。比如,攻击者可以通过伪造 X-Forwarded-For 请求标头来绕过基于 IP 的频率限制、访问控制。更进一步,攻击者还可以构造具有危害性 X-Forwarded-For 请求标头来对服务器进行多种攻击,例如注入恶意代码进行 XSS 攻击就是一种可能的行为。

正确做法

在使用 X-Forwarded-For 请求标头的值之前应该确保其内容可信。因为 X-Forwarded-For 请求标头一般由代理服务器添加,所以至少应该先确认请求来自可信的代理服务器才可使用 X-Forwarded-For 标头的值。

对于使用 CDN 的网站,如果服务器是 nginx,那么在 ngx_http_realip_module 模块的帮助下这一过程将会十分简单。只需编辑相应的 nginx 配置文件,在其中的 http、server 或 location 段,使用 set_real_ip_from 指定可信代理服务器地址(即 CDN 节点的地址),然后使用 real_ip_header 指明一个请求标头(即 X-Forwarded-For 标头),保存并重载 nginx 使配置生效。这样 nginx 收到请求后会检查请求的 REMOTE_ADDR 是否与 set_real_ip_from 相符,如果相符,nginx 会用 real_ip_header 对应标头的值替换 REMOTE_ADDR 原有的值,后端程序只需要正常使用 REMOTE_ADDR 的值即可。这种做法一般不需要改变后端程序。

参考资料