獲取使用者的真實ip

王滔發表於2015-06-17

常見的坑有兩個:

 

一、獲取的是內網的ip地址。在nginx作為反向代理層的架構中,轉發請求到php,java等應用容器上。結果php獲取的是nginx代理伺服器的ip,表現為一個內網的地址。php獲取REMOTE_ADDR就是這樣一個情況(內網地址)。

二、獲取的是攻擊者偽造的ip地址。攻擊者可以隨便偽造一個頭部資訊,隨便填寫一個ip放到頭部發過來,php獲取到HTTP_CLIENT_IP就是這樣一個情況。偽造的ip,導致我們資料庫儲存是假的ip,無從真實去判斷攻擊者的來源。比如批量註冊帳號的註冊ip,登入的ip等。

     為避免偽造,不要使用discuz原來的獲取ip函式,裡面的判斷優先順序會使得攻擊者容易偽造ip。

 

 

php程式碼:

 

 

 

function getIP() {

 

        if (getenv("HTTP_X_FORWARDED_FOR")) {

            //這個提到最前面,作為優先順序,nginx代理會獲取到使用者真實ip,發在這個環境變數上,必須要nginx配置這個環境變數HTTP_X_FORWARDED_FOR

            $ip = getenv("HTTP_X_FORWARDED_FOR");

        } else if (getenv("REMOTE_ADDR")) {

           //在nginx作為反向代理的架構中,使用REMOTE_ADDR拿到的將會是反向代理的的ip,即拿到是nginx伺服器的ip地址。往往表現是一個內網ip。

            $ip = getenv("REMOTE_ADDR");

        } else if ($_SERVER['REMOTE_ADDR']) {

            $ip = $_SERVER['REMOTE_ADDR'];

        } else if (getenv("HTTP_CLIENT_IP")) {

            //HTTP_CLIENT_IP攻擊者可以偽造一個這樣的頭部資訊,導致獲取的是攻擊者隨意設定的ip地址。

            $ip = getenv("HTTP_CLIENT_IP");

        } else {

            $ip = "unknown";

        }

        return $ip;

    }

 

 

 

說明

 

在反向代理架構中,不能通過REMOTE_ADDR來獲取使用者的真實ip!

 

以前的理解方式有誤(更新一下)

一般的方式是這樣:

nginx>>(fastcgi方式)>>php引擎

nginx把REMOTE_ADDR傳遞給了php。代表的是當前與nginx通訊的客戶端ip,一般情況下(非反向代理),這個客戶就是使用者的瀏覽器,所以得到的使用者的ip。


假設做了反向代理架構,是下面這樣子的:


用 戶>>>>>>>>>>>伺服器 a>>>>>>>>>>>>>>>>>>nginx>>>>>>>>>>>>>> (fastcgi方式通訊)>>>>>>>>>>>>php引擎


   使用者訪問一個域名,實際上是通過伺服器a做了轉發,轉發到nginx去(反向代理架構經常會這樣部署)


  於是,當前與nginx通訊的客戶端,就是伺服器a的地址, REMOTE_ADDR就是a伺服器的地址了。


  如何判斷:nginx的上一層是不是還有一層。像上面的情況就還有一層。所以得到的將會是伺服器a的地址。

 

 

 

總結:在nginx作為反向代理的架構中,php的REMOTE_ADDR(其他語言也是類似的名稱)拿到的將會是nginx代理的ip地址。拿不到使用者的真實ip,拿到是nginx反向代理伺服器地址。

 

REMOTE_ADDR本意就是遠端的地址,nginx是代理層,轉發請求到php,php獲取到的遠端地址實際上是nginx反向代理伺服器ip,這是符合協議規則的。

 

但是,可以讓nginx幫助我們拿到使用者的真實ip,寫到一個環境變數中,然後轉發給我們,只要按照某個約定的名稱即可,比如約定名稱為HTTP_X_FORWARD_FOR(也可以約定其他名稱,關鍵看nginx中配置,可以全公司考慮統一)

 

 

nginx配置類似於這樣:

 

fastcgi_param  HTTP_X_FORWARD_FOR  $remote_addr;

 

 上一句的目的是,將HTTP_X_FORWARD_FOR的值設定為$remote_addr的值。也就是將使用者真實的ip(或使用者使用代理的ip)放到HTTP_X_FORWARD_FOR中去。

 

 $remote_addr是nginx的內建變數,這個變數它得到是使用者真實的ip地址(使用者使用了代理,則就是代理的ip地址)。

 

於是在php端通過getenv("HTTP_X_FORWARDED_FOR")就可以獲取到nginx傳遞過來的值,是使用者真實的ip地址。

相關文章