X-Forwarded-For和相關幾個頭部的理解
-
$remote_addr
是nginx與客戶端進行TCP連接過程中,獲得的客戶端真實地址. Remote Address 無法偽造,因為建立 TCP 連接需要三次握手,如果偽造了源 IP,無法建立 TCP 連接,更不會有后面的 HTTP 請求 -
X-Real-IP
是一個自定義頭。X-Real-Ip 通常被 HTTP 代理用來表示與它產生 TCP 連接的設備 IP,這個設備可能是其他代理,也可能是真正的請求端。需要注意的是,X-Real-Ip 目前並不屬於任何標准,代理和 Web 應用之間可以約定用任何自定義頭來傳遞這個信息 -
X-Forwarded-For
X-Forwarded-For 是一個擴展頭。HTTP/1.1(RFC 2616)協議並沒有對它的定義,它最開始是由 Squid 這個緩存代理軟件引入,用來表示 HTTP 請求端真實 IP,現在已經成為事實上的標准,被各大 HTTP 代理、負載均衡等轉發服務廣泛使用,並被寫入 RFC 7239(Forwarded HTTP Extension)標准之中.
X-Forwarded-For請求頭格式非常簡單,就這樣:
X-Forwarded-For:client, proxy1, proxy2
可以看到,XFF 的內容由「英文逗號 + 空格」隔開的多個部分組成,最開始的是離服務端最遠的設備 IP,然后是每一級代理設備的 IP。
如果一個 HTTP 請求到達服務器之前,經過了三個代理 Proxy1、Proxy2、Proxy3,IP 分別為 IP1、IP2、IP3,用戶真實 IP 為 IP0,那么按照 XFF 標准,服務端最終會收到以下信息:
X-Forwarded-For: IP0, IP1, IP2
Proxy3 直連服務器,它會給 XFF 追加 IP2,表示它是在幫 Proxy2 轉發請求。列表中並沒有 IP3,IP3 可以在服務端通過 remote_address 來自 TCP 連接,表示與服務端建立 TCP 連接的設備 IP,在這個例子里就是 IP3。
詳細分析一下,這樣的結果是經過這樣的流程而形成的:
- 用戶IP0---> 代理Proxy1(IP1),Proxy1記錄用戶IP0,並將請求轉發個Proxy2時,帶上一個Http Header
X-Forwarded-For: IP0
- Proxy2收到請求后讀取到請求有
X-Forwarded-For: IP0
,然后proxy2 繼續把鏈接上來的proxy1 ip追加到 X-Forwarded-For 上面,構造出X-Forwarded-For: IP0, IP1
,繼續轉發請求給Proxy 3 - 同理,Proxy3 按照第二部構造出
X-Forwarded-For: IP0, IP1, IP2
,轉發給真正的服務器,比如NGINX,nginx收到了http請求,里面就是X-Forwarded-For: IP0, IP1, IP2
這樣的結果。所以Proxy 3 的IP3,不會出現在這里。 - nginx 獲取proxy3的IP 能通過
remote_address就是真正建立TCP鏈接的IP,這個不能偽造,是直接產生鏈接的IP。$remote_address 無法偽造,因為建立 TCP 連接需要三次握手,如果偽造了源 IP,無法建立 TCP 連接,更不會有后面的 HTTP 請求。
x-forwarded-for 實踐研究:
-
uwsgi_pass的情況下,nginx 沒有設置proxy_pass x-forwarded-for: $proxy_add_x_forwarded_for;
如果請求頭傳了XFF,在flask里面能正常讀取請求頭里面的XFF,就是當是一個普通的頭讀出;如果header不傳這個XFF的話,就讀不到 -
proxy_pass 情況下
-
沒有傳 # proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for 的話,跟上面的uwsgi_pass 一樣,都是在沒有設置header XFF情況下,讀不到。
-
如果傳了 proxy_set_header X-Forwarded-For
remote_address),因為這句proxy_set_header 會讓nginx追加一個$remote_address到XFF。
-
header 傳xff的話, 程序里面可以讀到Xff 頭: X-Forwarded-For: 188.103.19.120, 10.0.2.2 (第一個是我自己編的,第二個是
proxy_add_x_forwarded_for 這句而追加$remote_addr到XFF。
總結:
-
只要nginx前端(例如lvs, varnish)轉發請求給nginx的時候,帶了x-forwarded-for ,那么程序就一定能讀到這個字段,如果nginx還設置了proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for, 那么程序能讀到XFF是:ip0, ip1 (客戶端Ip,lvs或者varnishIP)。 如果nginx沒有設置,那么nginx還是會原樣把http頭傳給程序,也就是說程序也能讀到XFF,而且XFF就是ip0 客戶端IP。
-
proxy_pass 設置這個頭 proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; 是站在一個作為代理的角度把。能繼續傳輸多級代理的頭。
-
nginx的日志格式寫了$http_x_forwared_for 說明前端(lvs)確實傳了這個頭過來。所以是程序是讀取到的
-
uwsgi_pass 不能設置 proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; 這個頭,是因為這個頭是對http代理來說,用來傳遞IP的,uwsgi 不可能充當一個代理。
-
nginx->程序,這里其實有兩個鏈接過程,其他IP與nginx的TCP鏈接, nginx與程序的TCP鏈接。所以$remote_addr都是對各自來說的。
程序的remote_addr: remote_addr 127.0.0.1 (跟它鏈接的是nginx 內網127.0.0.1)
nginx的remote_addr : X-Real-Ip: 10.0.2.2 (跟它鏈接的是我的電腦,IP 10.0.2.2)
-
對程序來說,讀取的request.remote_addr 也永遠是直接跟他鏈接的ip, 也就是反向代理nginx
-
The access_route attribute uses the X-Forwarded-Forheader, falling back to the REMOTE_ADDRWSGI variable; 也就是說access_route默認讀取XFF頭,如果沒有,降級讀取WSGI的REMOTE_ADDR變量,這個 WSGI的REMOTE_ADDR變量 就是 $remote_addr
-
request.envron 是WSGI的變量,都是wsgi server轉過來的,普通的頭都是加了HTTP_前綴的 ,包括proxy_set_header Host
proxy_add_x_forwarded_for;
添加的頭都會出現在處理,因為他們就是普通的http頭 -
LVS->nginx的情況下, 請求的時候主動加XFF,程序讀取的時候沒顯示。因為LVS設置XFF的時候,直接把直連的IP賦值給LVS,忽略掉所有本來有的XFF,要從LVS這里開始。 所以程序讀到的XFF是 :XFF headers 218.107.55.254, 10.120.214.252
前面的是我的IP, 后面的是LVS的IP
{ "wsgi.multiprocess": "False", "SERVER_SOFTWARE": "Werkzeug/0.11.10", "SCRIPT_NAME": "", "REQUEST_METHOD": "GET", "PATH_INFO": "/api/get_agreement_url/", "SERVER_PROTOCOL": "HTTP/1.0", "QUERY_STRING": "", "werkzeug.server.shutdown": "<function shutdown_server at 0x7f4a2f4e5488>", "CONTENT_LENGTH": "", "SERVER_NAME": "127.0.0.1", "REMOTE_PORT": 58284, "werkzeug.request": "", "wsgi.url_scheme": "http", "SERVER_PORT": "6000", "HTTP_POSTMAN_TOKEN": "666cfd97-585b-c342-f0bd-5c785dfff27d", "wsgi.input": "", "wsgi.multithread": "False", "HTTP_CACHE_CONTROL": "no-cache", "HTTP_ACCEPT": "*/*", "wsgi.version": "(1, 0)", "wsgi.run_once": "False", "wsgi.errors": "", "CONTENT_TYPE": "", "REMOTE_ADDR": "127.0.0.1", "HTTP_CONNECTION": "close", "HTTP_USER_AGENT": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.36", "HTTP_ACCEPT_LANGUAGE": "zh-CN,zh;q=0.8,en;q=0.6", "HTTP_X_FORWARDED_FOR": "10.0.2.2", "HTTP_ACCEPT_ENCODING": "gzip, deflate, sdch", "HTTP_HOST": "[test.mumu.nie.netease.com:8000](http://test.mumu.nie.netease.com:8000/)", }
remote_address 到 xff頭,如果xff頭不存在,那么xff就被設置成跟$remote_address 一樣了。如果本來就存在,就追加了 ip1, ip2這樣的形式
問題:
- 為什么lvs-nginx , nginx的日志記錄$http_x_forwarded_for還是可以的?
鏈接:https://www.jianshu.com/p/15f3498a7fad