X-Forwarded-For

HTTP 請求頭中的 X-Forwarded-For

0.3 2018.05.22 11:40* 字數 1768 閱讀 21691評論 0

X-Forwarded-For和相關幾個頭部的理解

  • $remote_addr
    是nginx與客戶端進行TCP鏈接過程當中,得到的客戶端真實地址. Remote Address 沒法僞造,由於創建 TCP 鏈接須要三次握手,若是僞造了源 IP,沒法創建 TCP 鏈接,更不會有後面的 HTTP 請求php

  • 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)標準之中.nginx

X-Forwarded-For請求頭格式很是簡單,就這樣:json

X-Forwarded-For:client, proxy1, proxy2 

能夠看到,XFF 的內容由「英文逗號 + 空格」隔開的多個部分組成,最開始的是離服務端最遠的設備 IP,而後是每一級代理設備的 IP。flask

若是一個 HTTP 請求到達服務器以前,通過了三個代理 Proxy一、Proxy二、Proxy3,IP 分別爲 IP一、IP二、IP3,用戶真實 IP 爲 IP0,那麼按照 XFF 標準,服務端最終會收到如下信息:api

X-Forwarded-For: IP0, IP1, IP2 

Proxy3 直連服務器,它會給 XFF 追加 IP2,表示它是在幫 Proxy2 轉發請求。列表中並無 IP3,IP3 能夠在服務端經過 $remote_address 字段得到。咱們知道 HTTP 鏈接基於 TCP 鏈接,HTTP 協議中沒有 IP 的概念,$remote_address 來自 TCP 鏈接,表示與服務端創建 TCP 鏈接的設備 IP,在這個例子裏就是 IP3。緩存

詳細分析一下,這樣的結果是通過這樣的流程而造成的:服務器

  1. 用戶IP0---> 代理Proxy1(IP1),Proxy1記錄用戶IP0,並將請求轉發個Proxy2時,帶上一個Http Header
    X-Forwarded-For: IP0
  2. Proxy2收到請求後讀取到請求有 X-Forwarded-For: IP0,而後proxy2 繼續把連接上來的proxy1 ip追加到 X-Forwarded-For 上面,構造出X-Forwarded-For: IP0, IP1,繼續轉發請求給Proxy 3
  3. 同理,Proxy3 按照第二部構造出 X-Forwarded-For: IP0, IP1, IP2,轉發給真正的服務器,好比NGINX,nginx收到了http請求,裏面就是 X-Forwarded-For: IP0, IP1, IP2 這樣的結果。因此Proxy 3 的IP3,不會出如今這裏。
  4. nginx 獲取proxy3的IP 能經過$remote_address獲取到,由於這個$remote_address就是真正創建TCP連接的IP,這個不能僞造,是直接產生連接的IP。$remote_address 沒法僞造,由於創建 TCP 鏈接須要三次握手,若是僞造了源 IP,沒法創建 TCP 鏈接,更不會有後面的 HTTP 請求。

x-forwarded-for 實踐研究:

  1. uwsgi_pass的狀況下,nginx 沒有設置proxy_pass x-forwarded-for: $proxy_add_x_forwarded_for;
    若是請求頭傳了XFF,在flask裏面能正常讀取請求頭裏面的XFF,就是當是一個普通的頭讀出;若是header不傳這個XFF的話,就讀不到負載均衡

  2. proxy_pass 狀況下ui

  • 沒有傳 # proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for 的話,跟上面的uwsgi_pass 同樣,都是在沒有設置header XFF狀況下,讀不到。

  • 若是傳了 proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for, header 不傳xff 的話,也是能夠在程序裏面讀到Xff 頭: X-Forwarded-For: 10.0.2.2 (這個IP就是真正連上nginx 的IP, 也就是$remote_address),由於這句proxy_set_header 會讓nginx追加一個$remote_address到XFF。

  • header 傳xff的話, 程序裏面能夠讀到Xff 頭: X-Forwarded-For: 188.103.19.120, 10.0.2.2 (第一個是我本身編的,第二個是$remote_address),nginx仍是會由於proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for 這句而追加$remote_addr到XFF。

總結:

  1. 只要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。

  2. proxy_pass 設置這個頭 proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; 是站在一個做爲代理的角度把。能繼續傳輸多級代理的頭。

  3. nginx的日誌格式寫了$http_x_forwared_for 說明前端(lvs)確實傳了這個頭過來。因此是程序是讀取到的

  4. uwsgi_pass 不能設置 proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; 這個頭,是由於這個頭是對http代理來講,用來傳遞IP的,uwsgi 不可能充當一個代理。

  5. 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)

  1. 對程序來講,讀取的request.remote_addr 也永遠是直接跟他連接的ip, 也就是反向代理nginx

  2. 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

  3. request.envron 是WSGI的變量,都是wsgi server轉過來的,普通的頭都是加了HTTP_前綴的 ,包括proxy_set_header Host $host:8000;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    添加的頭都會出如今處理,由於他們就是普通的http頭

  4. 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/)", } 

$proxy_add_x_forwarded_for; nginx的這個變量含義就是,每次都追加$remote_address 到 xff頭,若是xff頭不存在,那麼xff就被設置成跟$remote_address 同樣了。若是原本就存在,就追加了 ip1, ip2這樣的形式

問題:

爲何lvs-nginx , nginx的日誌記錄$http_x_forwarded_for仍是能夠的

本站公眾號
   歡迎關注本站公眾號,獲取更多信息