本帖最後由 IT_man 於 2015-7-3 10:47 編輯
# P4 n+ V# j' Z$ C8 O
: K$ D. Z/ C1 K% d8 E很多網站都會有偵測使用者 IP 的功能,不管是判斷使用者來自哪邊,或者是記錄使用者的位置。但是你知道嗎?網路上大多數的教學全部都是「錯誤」的。正確的程式寫法可以確保知道訪客的 IP,但是錯誤的寫法卻可能讓網站管理者永遠不知道犯罪者的來源。; `/ O0 d# {1 h
$ G$ {3 S0 S' v1 l' ~這次我們單就偵測 IP 的議題來探討各種錯誤的寫法。4 d" a+ z6 Y2 D2 Z: J/ a- P' x
- V! t9 W9 V! _8 Q( m2 \
4 M' [0 z u( I& g你知道網路上的教學是不安全的嗎?; Z+ ^2 K5 {: l n5 l% V3 V Z
我們先來看一下網路上的教學,讓我們 Google 找一下「PHP 取得 IP」,就可以看到許多人熱心的教學,我們隨意挑一個常見的教學來看看。
: E% [, W( E9 R9 |0 O/ [! }以 PHP 為例:" f8 D1 x3 m% k! d4 Z: A4 ?# `
- <?php
5 u* \3 i8 [2 f8 j1 T: z' I) t- n - if(!empty($_SERVER['HTTP_CLIENT_IP'])){4 K, y- f8 b4 |& \& [
- $myip = $_SERVER['HTTP_CLIENT_IP'];
& r* ~5 `6 Y0 B* E" A; M' N* Z - }else if(!empty($_SERVER['HTTP_X_FORWARDED_FOR'])){
+ D9 V5 A9 h6 S3 m7 Y4 T - $myip = $_SERVER['HTTP_X_FORWARDED_FOR'];. K! W* @) [" V$ x" l" V- m
- }else{
9 {! c' D; Y, @4 ^- E$ x - $myip= $_SERVER['REMOTE_ADDR'];
/ s; O$ m) u/ l' | - }" i$ m+ C1 W8 ^/ m, x; ~
- echo $myip;
2 H* E% M/ g* N# o. W - ?>
複製代碼 . v9 }9 @3 S: x
b+ B a2 j# V' P/ q6 Y. Z
這是一個很基本的寫法、很正確的想法,如果 HTTP Header 中包含「Client-IP」,就先以他當作真實 IP。若包含「X-Forwarded-For」,則取他當作真實 IP。若兩者都沒有,則取「REMOTE_ADDR」變數作為真實 IP。因為當使用者連線時透過代理伺服器時,REMOTE_ADDR 會顯示為代理伺服器 Proxy 的 IP。部分代理伺服器會將使用者的原始真實 IP 放在 Client-IP 或 X-Forwarded-For header 中傳遞,如果在變數中呼叫則可以取得真實 IP。
( y! E3 p+ t# E8 m3 x; U, p* s但是你知道嗎?網路上 80% 的教學寫法全部都是「錯誤」的。
: X; {/ E8 G2 m9 U2 S3 U
7 q2 z, }4 ?" D( o3 Y為什麼這樣說呢?請大家記得一件事情:「任何從客戶端取得的資料都是不可信任的!」% `/ F$ `% l3 e. [! _0 R
6 U- l* o. }& Z4 e! i
竄改 HTTP Header「X-Forwarded-For」這個變數雖然「有機會」取得使用者的真實 IP,但是由於這個值是從客戶端傳送過來的,所以「有可能」被使用者竄改。& h0 P+ v& `3 x3 T, [' l
舉例來說,我寫了一個小程式,偵測這些常見的 HTTP Header 判斷 IP。並且使用 Burp Suite 這個工具來修改 HTTP Request。$ _$ v: G$ N6 W, A) X# d
" d0 ~* x" k1 c6 U- k( n) G& X- b7 G: _
頁面上顯示目前我目前的 IP「49.50.68.17」,並且其他的 header 是空的。但如果我今天使用 Burp Suite 之類的 Proxy 工具自行竄改封包,加上 X-Forwarded-For 或是 Client-IP header:% O3 M0 z7 F$ i, |" q

+ I' {6 C/ S7 Y/ F修改完畢之後,再到原本的顯示 IP 介面,會發現網頁錯將我竄改的 header 當作正確的資料填入。
* u9 m+ @. [; H! F h" T! Q : }4 d2 g/ T- |2 t" w
1 C( Q1 w- x! C; T, Z; j& ]
使用代理伺服器 Proxy 的情況使用代理伺服器的情況下,HTTP Header 會有不同的行為。例如 Elite Proxy 如何隱藏客戶端的真實 IP。以下簡單介紹幾種常見的狀況給各位參考。
* ?* f* s* _8 ^! g6 a: b8 J直接連線 (沒有使用 Proxy)' _, r Y; [ [- r$ N4 ]. w" c
; O3 J) v+ }! {8 T8 ?' V - REMOTE_ADDR: 客戶端真實 IP
- HTTP_VIA: 無
- HTTP_X_FORWARDED_FOR: 無
& r! f2 K) m Y e" V, u2 T, _4 z Transparent Proxy9 [! ~7 `( K, T6 t4 R1 ]& a
& J: e4 I! t% m
- REMOTE_ADDR: 最後一個代理伺服器 IP
- HTTP_VIA: 代理伺服器 IP
- HTTP_X_FORWARDED_FOR: 客戶端真實 IP,後以逗點串接多個經過的代理伺服器 IP$ |8 p: K3 m8 y% g" d( \! B
Anonymous Proxy+ k) K' n4 i) L' y& L* r5 Z
. ], U3 b! A- K2 }" ?1 ]( b4 m0 u - REMOTE_ADDR: 最後一個代理伺服器 IP
- HTTP_VIA: 代理伺服器 IP
- HTTP_X_FORWARDED_FOR: 代理伺服器 IP,後以逗點串接多個經過的代理伺服器 IP
9 L" u4 r. w+ }' T8 O High Anonymity Proxy (Elite Proxy)% G8 | ~' m4 F. E' ^
8 S5 s/ y' H/ J) j - REMOTE_ADDR: 代理伺服器 IP
- HTTP_VIA: 無
- HTTP_X_FORWARDED_FOR: 無 (或以逗點串接多個經過的代理伺服器 IP)
% w0 r6 ], T6 h$ ]( [ 實際情況在我們測試的過程中,通常我們都會讓瀏覽器自帶 X-Forwarded-For,並且自行填入 IP。常常會發現有一些網站出現如下的警告…
0 r' Z' O. W8 n$ g# o- _$ }2 \ # B& J2 B; y4 E& X: E' |
有沒有搞錯?「上次登入位置 127.0.0.1」?沒錯,這個是知名論壇套件「Discuz!」的功能,抓取 IP 的功能也是不安全的寫法。也有這樣的經驗,之前開著 X-Forwarded-For 的 header 到一些網站,竟然直接出現管理者後台!/ b" U/ t( k$ s/ y) n2 T; U
你覺得只有一般人撰寫的程式會有這樣的問題嗎?其實大型網站也可能會有類似的問題:
$ @2 f% g! \$ k! M" h
$ `& f6 y6 z% f& }" M: ]先不論為什麼 127.0.0.1 會在美國,這樣的寫法可能會讓管理者永遠抓不到犯罪者的真實 IP,甚至攻擊者可以竄改 header 插入特殊字元,對網站進行 SQL Injection 或者 Cross-Site Scripting 攻擊。
. S8 n6 G' K" m9 O
$ e5 |* E: s% u1 n! q) m2 M正確又安全的方式「任何從客戶端取得的資料都是不可信任的!」
$ d* l) n2 o5 j$ e請各位開發者、管理者記住這個大原則,雖然這些 Request Header 可能含有真實 IP 的資訊,但是因為他的安全性不高,因此我們絕對不能完全信賴這個數值。) B$ W$ A, i& c3 G6 V; C
那我們該怎麼處理呢?我的建議是記錄所有相關的 header 欄位存入資料庫,包含「REMOTE_ADDR」「X-Forwarded-For」等等,真正有犯罪事件發生時,就可以調出所有完整的 IP 資訊進行人工判斷,找出真正的 IP。當然從 header 存入的數值也可能會遭到攻擊者竄改插入特殊字元嘗試 SQL Injection,因此存入值必須先經過過濾,或者使用 Prepared Statement 進行存放。
+ t4 a9 P5 @+ _3 x1 m' }) W可以參考的 HTTP Header(依照可能存放真實 IP 的順序)* HTTP_CLIENT_IP* HTTP_X_FORWARDED_FOR* HTTP_X_FORWARDED* HTTP_X_CLUSTER_CLIENT_IP* HTTP_FORWARDED_FOR* HTTP_FORWARDED* REMOTE_ADDR (真實 IP 或是 Proxy IP)* HTTP_VIA (參考經過的 Proxy)% i4 X) B) f4 U0 D$ v2 S: A
「駭客思維」就是找出網站任何可能竄改的弱點,從網頁上的元素到 HTTP Header 都是嘗試的對象。因此身為防禦者一定要清楚的知道哪些數值是不能信賴的,不要再參考網路上錯誤的教學了!
7 K! r1 C- i$ }, Z0 }* m
" \" T6 g+ Q: ^/ H# Q! X0 Z*原文參考 CEO Allen Own 大作 http://devco.re/blog/2014/06/19/client-ip-detection/% |8 g( N) [7 v4 ^0 m' Q% O0 N8 t% ?
|
|