伺服器獲取真實客戶端 IP

m9rco發表於2018-12-08

0x01 先查個問題

測試環境微信支付通道提示網路環境未能通過安全驗證,請稍後再試,出現這種情況一般首要 想到可能是雙方網路互動中微信方驗參與我們出現不一致,翻了下手冊確定是這類問題開始排查環節

  1. 可能獲取真實IP方式錯誤

    1. getenv('HTTP_CLIENT_IP')
    2. getenv('HTTP_X_FORWARDED_FOR')
    3. getenv('REMOTE_ADDR')
    4. filter_var($remote_ip, FILTER_VALIDATE_IP)
    5. 已經依次獲取並過濾
    6. 固程式沒有任何問題,往上發散
  2. 是否反向代理

    經過反向代理後,由於在客戶端和web伺服器之間增加了中間層,因此web伺服器無法直接拿到客戶端的ip,只能通過$remote_addr變數拿到的將是反向代理伺服器的ip地址,檢查不存在此類問題,再往上,擅長網路通訊工程的同學表示絕不認輸

  3. 可能NAT分配出口IP,或負載均衡服務分發出現異常

    1. 先拿到我本地內網外網IP 方便之後問題排查
    # 本機IP
    ifconfig | grep -A 1 "en" | grep broadcast | cut -d " " -f 2
    # 外網IP
    curl --silent http://icanhazip.com
    複製程式碼
    1. 檢查與80埠建立連線目標都有誰
     netstat -tn|grep 80|akw '{print $5}'|awk -F '{print $1}' | grep [本地IP]
    複製程式碼

    這裡出現問題,竟然沒有我的IP,再以nginx $remote_addr拿到的IP作為參考,這是nginx最後一次握手的IP,$remote_addr = 10.168.0.0/16

    nginx處列印$remote_addr,並在server_name新增當前機器ip,分別以負載均衡IP與本地IP做測試,最終確定問題出現在負載均衡伺服器出現異常


0x02 LNMP棧拿真實IP

LNMP棧內PHP所有獲得到的TCP操作資訊都是由前面Nginx通過fastcgi傳遞給它的,就比如$_SERVER['REMOTE_ADDR']include fastcgi.conf;引進,其等於nginx$remote_addr

Nginx中的幾個變數:

  • $remote_addr

    代表客戶端的IP,但它的值不是由客戶端提供的,而是服務端根據客戶端的ip指定的,icanhazip的原理也是這樣, 當你的瀏覽器訪問某個網站時,假設中間沒有任何代理,那麼網站的web伺服器就會把remote_addr設為你在公網暴露的IP,如果你用了某個代理,那麼你的瀏覽器會先訪問這個代理,然後再由這個代理轉發到網站,這樣web伺服器就會把remote_addr設為這臺代理機器的IP, 除非代理將你的IP附在請求header中一起轉交給web伺服器。

  • $proxy_add_x_forwarded_for

    $proxy_add_x_forwarded_for變數包含客戶端請求頭中的"X-Forwarded-For",與$remote_addr兩部分,他們之間用逗號分開。X-Forwarded-For(簡稱XFF),X-Forwarded-For 是一個 HTTP 擴充套件頭部。RFC 2616 協議並沒有對它的定義,它最開始是由 Squid 這個快取代理軟體引入,用來表示 HTTP 請求端真實 IP。如今它已經成為事實上的標準,被各大HTTP 代理、負載均衡等轉發服務廣泛使用,並被寫入 RFC 7239(Forwarded HTTP Extension` 標準之中。

  • $proxy_set_header

    已在排查問題中說明,可設定代理後 header

      proxy_set_header Host $http_host;
      proxy_set_header X-Real-IP $remote_addr;
      proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
      proxy_set_header X-Forwarded-Proto $scheme;
    複製程式碼
  • X-Real-IP

    一般比如X-Real-IP這一個自定義頭部欄位,通常被 HTTP 代理用來表示與它產生TCP 連線的裝置 IP,這個裝置可能是其他代理,也可能是真正的請求端,這個要看經過代理的層級次數或是是否始終將真實IP一路傳下來。(牢記:任何客戶端傳上來的東西都是不可信的)

當多層代理或使用CDN時,如果代理伺服器不把使用者的真實IP傳遞下去,那麼伺服器將永遠不可能獲取到使用者的真實IP。

0x03 使用者的真實IP從何而來

  1. 寬頻供應商提供獨立IP 比如家裡電信寬頻上網,電信給分配了公網ip,那麼一個請求經過的ip路徑如下:

    伺服器獲取真實客戶端 IP

    這種情況下,119.147.19.234 會把得到的116.1.2.3附加到頭資訊中傳給10.168.0.0/32,因此這種情況下,我們取得的使用者ip則為:116.1.2.3。 如果119.110.0.0/16沒有把116.1.2.3附加到頭資訊中傳給業務伺服器,業務伺服器就只能取上上一級ip地址

  2. 寬頻供應商不能提供獨立IP

    寬頻提供商沒有足夠的公網ip,分配的是個內網ip,比如長寬等小的isp。請求路徑則可能如下:

    伺服器獲取真實客戶端 IP

    這種情況下得到的使用者ip,就是211.162.78.1。 這種情況下,就可能出現一個ip對應有數十上百個使用者的情況了

  3. 手機2g上網

網路提供商沒法直接提供ip給單個使用者終端,以中國移動cmwap上網為例,因此請求路徑可能為:

伺服器獲取真實客戶端 IP
這種情況下得到的使用者ip,就是202.96.75.1。2008年的時候整個廣東聯通就三個手機上網的公網ip,因此這種情況下,同一ip出現數十萬使用者也是正常的。

  1. 有幾萬或數十萬員工的公司

    這種也會出現來自同一ip的超多使用者,可能達到幾萬人,但出口IP可能就那麼幾個。


0x04 NAT [Network Address Translation]

中文意思是網路地址轉換,它允許一個整體機構以一個公用IP地址出現在Internet上。

NATOSI參考模型的網路層 (第3層), 它是一種把內部私有網路地址(IP地址)翻譯成合法網路IP地址的技術。NAT可以讓那些使用私有地址的內部網路連線到Internet或其它IP網路上。NAT路由器在將內部網路的資料包傳送到公用網路時,在IP包的報頭把私有地址轉換成合法的IP地址。

RFC1918 規定了三塊專有的地址,作為私有的內部組網使用

  • A類:10.0.0.0 — 10.255.255.255 10.0.0.0/8
  • B類:172.16.0.0 — 172.31.255.255 172.16.0.0/12
  • C類:192.168.0.0 — 192.168.255.255 192.168.0.0/16

這三塊私有地址本身是可路由的,只是公網上的路由器不會轉發這三塊私有地址的流量;當一個公司內部配置了這些私有地址後,內部的計算機在和外網通訊時,公司的邊界路由會通過NAT或者PAT技術,將內部的私有地址轉換成外網IP,外部看到的源地址是公司邊界路由轉換過的公網IP地址,這在某種意義上也增加了內部網路的安全性

伺服器獲取真實客戶端 IP

這個過程是通過NAT中的本地址與全域性地址對映條目來實現的,所以事先要在NAT路由器上配置這樣的對映條目。

伺服器獲取真實客戶端 IP
通過這種方式一個公網 IP 底下可以發私有的 IP 地址。


0x05 IPV6 來了?

伺服器獲取真實客戶端 IP

寫這篇文章的時候看到有個推送,表示阿里全面應用IPV6,這件事的意義還挺重大的

伺服器獲取真實客戶端 IP

我們知道,一段 IPv4 標準的 IP 地址,一共由 4 X 8 = 32 位二進位制數字組成,理論上存在 2^32 個 IP 地址。等於 4,294,967,29642 億多個 IPv4 的地址。

伺服器獲取真實客戶端 IP

參考世界網際網路使用者統計報告,全球現在大概有4,208,571,287人在上網,也就是說已經快到ipv4地址設計的最大IP數了

不過不用擔心,前面提到的 NAT,讓 IPv4 公網 IP 哪怕用完了也能湊合過。

伺服器獲取真實客戶端 IP

到了 IPv6 ,相比 IPv4 最大的提升,就是位數大大增加,變成了 8 個 4 位的十六進位制數字。也就是說有 2^128IPv6 地址。地球上的每粒沙子都分一個也管夠

伺服器獲取真實客戶端 IP

儲存2^128位元組理論上什麼概念呢,在當今的量子水平下,假設計算裝置能夠操作在原子一級,每公斤質量可儲存大約10的25次方bits,儲存2的128次方的位元組大約需要272 trillion = 2720000億公斤

最後,週末愉快,北京聯通已經支援ipv6了,我在望京測試,可以拿到 ipv6地址

相關文章