PHP 的歷史悠久常常是優勢也是包袱。一個初來乍到的開發者,可能會因為希望實現某個功能而去 Google
PHP IP位址
值得慶幸的是,資安公司 DEVCORE 已經寫過一篇 如何正確的取得使用者的 IP,而且在 Google 的索引分數非常高。
然而,PHP 的教學常常見到「一鍋粥裡只有一粒粥,其它都是老鼠屎」的荒謬現象,以下隨便舉幾個例子:
這還僅僅只是 Google 第一頁的結果,其中甚至不乏 2017 年的文章。
在今天的文章開始之前,請各位務必先看過 DEVCORE 的 如何正確的取得使用者的 IP,我會從這篇文章的內容下去做擴充。
其實,上面被說成「錯誤」的 IP 取得範例,在設計時都有其理由。
最簡單的方式是直接判斷 $_SERVER['REMOTE_ADDR']
,這是在前面沒有設計 Load Balancer 或是使用類似 Cloudflare 為前提才適用的方式。
Remote Address 的定義是:最後一個來源的 IP 位址,它來自於 TCP 協定的的來源 IP,無法透過 HTTP Header 的方式修改,在不考慮被應用程式修改的前提下,這個值是可信任的。
然而,因為它是「最後一個來源」的 IP,也就是說如果我的應用程式躲在 Load Balancer、Reverse Proxy 或是使用 Cloudflare 之類的服務,此值就僅能取得這些設備的 IP。
|---------| |-------------|
| Client |======>| Application |
| 1.2.3.4 | | 11.11.11.11 |
|---------| |-------------|
狀況一:在 Application 伺服器上,利用 $_SERVER['REMOTE_ADDR'] 會拿到 1.2.3.4,這是我們要的
|---------| |---------------| |-------------|
| Client |======>| Reverse Proxy | ======>| Application |
| 1.2.3.4 | | 22.22.22.22 | | 11.11.11.11 |
|---------| |---------------| |-------------|
狀況二:在 Application 伺服器上,利用 $_SERVER['REMOTE_ADDR'] 會拿到 22.22.22.22,這不是我們要的
一般來說,Reverse Proxy 會把拿到的 $_SERVER['REMOTE_ADDR'] 放進另一個 HTTP Header(每個實作不儘相同,應該詳閱說明),通常會放在 $_SERVER['HTTP_X_FORWARDED_FOR']。
所以在這個例子中,Application 會拿到 $_SERVER['HTTP_X_FORWARDED_FOR'] = '1.2.3.4,22.22.22.22'(中間會用逗號分隔,以便有多個 Reverse Proxy 時可以使用)
也因為現代應用程式越來越多是放在 Reverse Proxy 或 Load Balance 之後,所以才會衍生出如上述那些「錯誤」的教學場景。
遺憾的是,$_SERVER['HTTP_X_FORWARDED_FOR']
是可以被偽造的,使用者只要任意填寫就可以達到偽造 IP 的手段。
在 Symfony Framework 中,提供了一個解決方案:TrustedProxies,藉由設定「可信任的 Proxies」來取得最一個使用者的來源 IP
以上述範例來說,我信任來自 22.22.22.22
的 Reverse Proxy,所以在 $_SERVER['HTTP_X_FORWARDED_FOR]
中會去除 22.22.22.22
這個 IP 之後,就是我希望取得的使用者 IP。
註:這個 IP 仍可能是偽造出來的,畢竟 Client 端可以經由任何的 Proxy Server 去存取網頁。這樣的技巧僅能確定自己的應用程式是沒問題的。
註:這個功能是可以自己實現的,不一定要倚賴 Symfony Framework,但是通常會建議有現成的解決方案就直接套用。
今天算是對 IP 的獲取做個補充,順便得罪人。