先上代碼
var http = require('http')
var server = http.createServer(function (req,res) {
console.log(req.headers['x-forwarded-for'] ); // 判斷是否有反向代理
console.log(req.socket.remoteAddress ); // 判斷后端的 socket 的 IP
let ip = req.headers['x-forwarded-for'] || req.socket.remoteAddress
res.end(ip)
})
server.listen('9098')
x-forwarded-for是什么?
X-Forwarded-For 是一個擴展header頭,用來表示 HTTP 請求端真實 IP,在HTTP/1.1(RFC 2616)協議中沒有定義,但是現在已經成為事實上的標准,被各大 HTTP 代理、負載均衡等轉發服務廣泛使用,並被寫入 RFC 7239(Forwarded HTTP Extension)標准之中。
由人為設置
一些代理服務器會設置一些消息頭,比如nginx會在轉發請求的時候可以帶上這個消息頭,向應用服務傳遞客戶端的真實IP;
使用下面的配置在nginx設置反向代理轉發的X-Forwarded-For:
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; // 常用
proxy_set_header X-Forwarded-For "$http_x_forwarded_for, $remote_addr" // 不常用,$http_x_forwarded_for是讀取到的消息頭,如果請求頭沒有x-Forwarded-for,這個值就是空的。
這樣就能在應用服務器拿到消息頭 X-Forwarded-For;
可以偽造
因為是人為設置,偽造一個假的很簡單,如下:
proxy_set_header X-Forwarded-For "121.12.12.12";
所以接收到的不可信任的服務器傳遞回來的header,或者客戶端偽造了header,其中x-forwarded-for是不能當做客戶端IP的。
格式
X-Forwarded-For請求頭格式非常簡單,就這樣:X-Forwarded-For:client, proxy1, proxy2
,由[英文逗號+空格]隔開的多個部分組成,最開始的是離服務端最遠的設備IP,然后是每一級代理的IP。
如果一個 HTTP 請求到達服務器之前,經過了三個代理 Proxy1、Proxy2、Proxy3,IP 分別為 IP1、IP2、IP3,用戶真實 IP 為 IP0,代理服務器會把前一個網絡設備的IP地址追加到X-Forwarded-For 上面,經過層層追加,服務端最終會收到以下信息:
X-Forwarded-For: IP0, IP1, IP2
同時注意到IP3是不會追加上到這個列表上的。
實際驗證:在本地電腦開啟一個nodejs服務,端口為9090;開啟nginx反向代理,端口為8062;
- 通過8062訪問nginx服務的時候,req.headers['x-forwarded-for']值為
192.168.1.105 //nginx通過X-Forwarded-For將客戶端的地址轉發了過來
- 通過9090直接訪問nodejs訪問,req.headers['x-forwarded-for']值為
undefined //直接請求的時候,沒有設置消息頭,自然為undefined
所以:
- 有沒有X-Forwarded-For和代理服務器的設置有關;
- 正確與否也和代理服務器有關;
remoteAddress
如果沒有X-Forwarded-For,應用服務器可以通過與服務端建立 TCP 連接獲取到。在nodejs中可以通過req.socket.remoteAddress獲取到IP3;
remoteAddress有沒有可能是假的呢?因為tcp鏈接需要三次握手,所以無法偽造這個ip。
X-Real-IP是什么
是一個自定義的消息頭,目前並不屬於任何標准,完全由用戶控制。
結論;
沒有代理:直接使用remoteAddress獲取客戶端IP,因為header中x-forwarded-for不可靠,可能有也可能沒有,甚至可能是偽造的;
有代理的情況下,獲取到的remoteAddress是代理服務器的IP,如果代理服務器是可信賴的,那么能通過x-forwarded-for來獲取客戶端IP。
在express,koa中都有獲取ip的方法,他們是怎么封裝的呢?
如有錯誤之處,望請斧正。