對於client->nginxreverseproxy->apache,
要想在程序中取得真實的IP,在執行nginx的configure時,必須指定參數“--with-http_realip_module”,示例:
./configure--prefix=/data/nginx--with-http_realip_module--with-stream--with-pcre=/tmp/X/pcre-8.32--with-openssl=/tmp/X/openssl-1.0.2a
參數說明:
--prefix=指定安裝目錄,也就是makeinstall後程序文件等的存放目錄
--with-http_realip_module使得程序可以通過環境變量HTTP_X_REAL_IP取得真實的客戶端IP地址
--with-stream表示啟用TCP代理
--with-pcre=指定依賴的pcre,注意為pcre源代碼解壓後的目錄路徑,而不是安裝路徑
--with-openssl=指定依賴的openssl,注意為openssl源代碼解壓後的目錄路徑,而不是安裝路徑
另外,最簡單的確認方法是使用nm命令查看nginx程序文件,看看是否有包含real相關的符號,對於版本nginx-1.9.4,可以發現存在“0809c54btngx_http_realip”。
測試程序代碼(後續測試基於它):
//g++-g-ohello.cgihello.cpp
#include<stdio.h>
#include<stdlib.h>
intmain()
{
printf("Content-Type:text/html;charset=utf-8\r\n\r\n");
printf("<p>HTTP_X_FORWARDED_FOR:%s\n",getenv("HTTP_X_FORWARDED_FOR"));
printf("<p>HTTP_X_REAL_IP:%s\n",getenv("HTTP_X_REAL_IP"));
printf("<p>REMOTE_ADDR:%s\n",getenv("REMOTE_ADDR"));
printf("<p>");
return0;
}
測試是在nginx自帶配置文件nginx.conf上進行的修改:
proxy_set_header可以添加在nginx.conf的http段,也可以是server段,還可以是location段,一級一級間是繼承和覆蓋關系。
示例:
location/{
#roothtml;
#indexindex.htmlindex.htm;
proxy_passhttp://20.61.28.11:80;
proxy_redirectoff;
proxy_set_headerHost$host;
proxy_set_headerX-Real-IP$remote_addr;#這個是必須的
proxy_set_headerX-Forwarded-For$proxy_add_x_forwarded_for;
}
X-Forwarded-For和X-Real-IP的區別是,如果請求時已帶了X-Forwarded-For,則nginx追加方式,這樣可以通過它顯示轉發的軌跡。
當然請求時完全可以構造假的X-Forwarded-For,在配置文件打開了X-Real-IP及編譯指定了--with-http_realip_module時,環境變量HTTP_X_REAL_IP總是為真實的客戶端IP。
如果是:
client->nginxreverseproxy(A)->nginxreverseproxy(B)->apache
HTTP_X_REAL_IP又會是什麼了?
假設如下部署:
client(10.6.81.39)->nginx(10.6.223.44:8080)->nginx(10.6.208.101:8080)->apache(10.6.208.101:80)
?A
假設nginx(10.6.223.44:8080)的配置均為(在nginx默認配置上的修改部分):
server{
listen8080;
server_name10.6.223.44;
location/{
#roothtml;
#indexindex.htmlindex.htm;
proxy_passhttp://10.6.208.101:8080;
proxy_redirectoff;
proxy_set_headerHost$host;
proxy_set_headerX-Real-IP$remote_addr;
proxy_set_headerX-Forwarded-For$proxy_add_x_forwarded_for;
}
假設nginx(10.6.208.101:8080)的配置均為(在nginx默認配置上的修改部分):
server{
listen8080;
server_name10.6.208.101;
location/{
#roothtml;
#indexindex.htmlindex.htm;
proxy_passhttp://10.6.208.101:80;
proxy_redirectoff;
proxy_set_headerHost$host;
proxy_set_headerX-Real-IP$remote_addr;
proxy_set_headerX-Forwarded-For$proxy_add_x_forwarded_for;
}
上述測試程序輸出的結果為:
<p>HTTP_X_FORWARDED_FOR:10.6.81.39,10.6.223.44
<p>HTTP_X_REAL_IP:10.6.223.44
<p>REMOTE_ADDR:10.6.81.39
?B
但如果client在HTTP請求頭中加入:
X-FORWARDED-FOR:8.8.8.7
CLIENT-IP:8.8.8.8
X-REAL-IP:8.8.8.10
後輸出結果變成:
<p>HTTP_X_FORWARDED_FOR:8.8.8.7,10.6.81.39,10.6.223.44
<p>HTTP_X_REAL_IP:10.6.223.44
<p>REMOTE_ADDR:8.8.8.7
?C
基於A,如果只nginx(10.6.223.44:8080)配置注釋掉“X-Forwarded-For”,輸出結果變成:
<p>HTTP_X_FORWARDED_FOR:10.6.223.44
<p>HTTP_X_REAL_IP:10.6.223.44
<p>REMOTE_ADDR:10.6.223.44
?D
基於A,如果只nginx(10.6.208.101:8080)配置注釋掉“X-Forwarded-For”,輸出結果變成:
<p>HTTP_X_FORWARDED_FOR:10.6.81.39
<p>HTTP_X_REAL_IP:10.6.223.44
<p>REMOTE_ADDR:10.6.81.39
基於測試1的配置,
當訪問路徑變成:client(10.6.81.39)->nginx(10.6.208.101:8080)->apache(10.6.208.101:80)時,程序輸出結果為:
<p>HTTP_X_FORWARDED_FOR:10.6.81.39
<p>HTTP_X_REAL_IP:10.6.81.39
<p>REMOTE_ADDR:10.6.81.39
但如果client在HTTP請求頭中加入:
X-FORWARDED-FOR:8.8.8.7
CLIENT-IP:8.8.8.8
X-REAL-IP:8.8.8.10
後輸出結果變成:
<p>HTTP_X_FORWARDED_FOR:8.8.8.7,10.6.81.39
<p>HTTP_X_REAL_IP:10.6.81.39
<p>REMOTE_ADDR:8.8.8.7
從上可以看出,只配置正確使用了real-ip功能,除HTTP_X_REAL_IP外,其它內容可以被干擾,client可以篡改它們。
如果正確編譯和配置了nginx反向代理,當只有一層nginx反向代理時,可以通過“HTTP_X_REAL_IP”取得client的真實IP。
如果有二層nginx反向代理,則client的真實IP被包含在“HTTP_X_FORWARDED_FOR”中。
最不可信的是“REMOTE_ADDR”,它的內容完全可以被client指定!總之只要編譯和配置正確,“HTTP_X_FORWARDED_FOR”總是包含了client的真實IP。