每家代理ip网站几乎都会开放一些免费的代理ip,然而这些ip又会在网站上有高匿和透明等不同标识,这两种的区别在于服务器端能否识别客户端的真实ip。

搭建Web服务

为了验证区别,首先用flask在服务器上搭建一个简单的Web服务,打印出请求头信息。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
# --*-- coding:utf-8 --*--

from flask import Flask, request

app = Flask(__name__)

@app.route('/')
def index():
    headers = request.headers
    print(headers)
    return 'test'


if __name__ == '__main__':
    app.run()

反向代理

用nginx为Web服务做反向代理

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
server {
    charset utf-8;
    listen 5555;
    server_name http://127.0.0.1;
    location / {
        proxy_pass http://127.0.0.1:5000;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    }
}

proxy_set_header可以更改或添加请求头信息,并转发到5000端口的后端服务。这里添加了三种常用配置。

  • Host
    服务器的主机ip

  • X-Real-IP
    remote_addr 赋值,从字面意思理解可能是客户端的真实ip,其实不然,这个只是相对于服务器来讲的上一节点机器的ip。

  • X-Forwarded-For
    这是一个http扩展头,最初是由Squid代理缓存软件引入,标准格式如下:

    X-Forwarded-For : client, proxy1, proxy2

    最左边为客户端ip,请求经过代理节点,便会在后边追加ip,以逗号分隔。假设最终 X-Forwarded-For 结果如上,nginx 中 remote_addr 得到的是 proxy2 的ip,而不是client的ip。

请求验证

分别使用不同的代理访问web服务。

不使用代理直接访问

Host 为我的服务器ip,X-Real-IP 和 X-Forwarded-For 都是我的本地客户端ip。

使用透明代理访问

X-Real-IP 变成了代理的地址,但 X-Forwarded-For 正如它的标准格式一样,包含了我的客户端ip信息,同时请求头中还会包含 Via 这样的请求头,暴露了代理的软件信息。所以透明代理并不能完全隐匿客户端的真实ip。

使用高匿代理访问

X-Real-IP 和 X-Forwarded-For 都变成了高匿代理的ip,X-Forwarded-For 没有客户端的ip信息,因而服务端是无法据此检测到此次请求是否使用了代理。

伪造请求头

这里有一个问题,既然 X-Real-IPX-Forwarded-For 都是扩展的请求头,那直接在请求中伪造这两个请求头会如何。

1
2
3
4
5
# 伪造请求头格式如下
headers = {
    "X-Real-Ip": "11.11.11.11",
    "X-Forwarded-For": "22.22.22.22",
}

使用透明代理访问

使用高匿代理访问

可以看到,影响甚微,就是在 X-Forwarded-For 的前端添加了伪造的ip,其他都没变。

结论

在由nginx做反向代理的Web服务中,“X-Real-Ip” 是由 nginx 的 remote_addr 变量赋值,remote_addr 记录的是与服务器进行TCP三次握手的“客户端”地址,不一定是用户的本机地址,同时这个参数也无法伪造。
X-Forwarded-For 记录了请求经过的所有代理ip地址(有些代理服务器或CDN等可能会隐藏地址),每经过一个代理节点,便在最后追加地址。如果请求伪造了这个字段,也只是在最左边添加了伪造信息,经过的代理节点地址依旧会被追加记录。