從連線IP來判斷單位內外時,會發現 RoR 的 log 檔的 IP 與前端 nginx 的 log 的 IP 有點不同,而且有設 proxy 及沒設 proxy 又有點不一樣,所以透過以下步驟來探索。
透過 proxy 觀察不同的 log
RoR 本身 web server 的 log 分別用 有設和沒設 proxy 出現的 log 是什麼,在 thin 或 mongrel 的 log/development.log 之中:
Processing DemoController#server_ip (for 3.4.5.6(proxy 宣稱為哪個 ip 代理) at 2008-09-27 21:13:16) [GET]
Processing DemoController#server_ip (for 1.2.3.4(家裡ip) at 2008-09-27 21:18:38) [GET]
竟然沒有出現真正與 RoR 連線的 proxy IP 出來,再看一下 nginx 的 log :
2.3.4.5(連過來的 proxy IP) - - [27/Sep/2008:21:13:16 +0800] GET /demo/server_ip HTTP/1.0 "200" 4492 "-" "Mozilla/5.0 (Windows; U; Windows NT 5.1; zh-TW; rv:1.8.1.11) Gecko/20071127 Firefox/2.0.0.11" "3.4.5.6"(proxy 宣稱為哪個 ip 代理)
1.2.3.4(家裡ip) - - [27/Sep/2008:21:18:38 +0800] GET /demo/server_ip HTTP/1.1 "200" 4300 "-" "Mozilla/5.0 (Windows; U; Windows NT 5.1; zh-TW; rv:1.9.0.3) Gecko/2008092417 Firefox/3.0.3" "-"
從上面nginx log 會看到為何 user agent 是不一致?其實是 proxy 所偽造的,在此簡述一下連線測試的方式,圖解就比較清楚:
從家裡 1.2.3.4 透過 proxy 2.3.4.5 來連到 web server ,通常 proxy 本身會有個環境變數說是為 1.2.3.4 代理的 IP,但是 proxy 可以設定成顯示是為 3.4.5.6 代理的 IP,後面會簡述一下 squid 如何設定。
上述的第一行 ngix 的 log 中的第一欄 remote_addr 是連接的 IP ,第一行是有透過 proxy ,所以最後一欄 proxy_add_x_forwarded_for 是有 proxy 告訴 nginx 說是幫背後的哪一個 IP 來做代理。而第二行則是沒透過 proxy ,所以最一後欄出現 '-'。
檢視 nginx 設定
在 nginx 的有關 proxy IP 設定中
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
$proxy_add_x_forwarded_for 是可以透過 user 連過來的 proxy 所造假,所以不可信任該值。
apache 的 HTTP 環境變數查詢
開始有點搞不清楚了,到底從 RoR 要抓哪個變數,先從最單純的環境測試一下,先在 apache 上利用 perl script查詢有哪些環境變數,分別以有或沒有設 proxy:
#!/usr/bin/perl
print "Content-type: text/plain\n\n";
foreach $var (sort(keys(%ENV))) {
$val = $ENV{$var};
$val =~ s|\n|\\n|g;
$val =~ s|"|\\"|g;
print "${var}=\"${val}\"\n";
}
與 user 端有關 IP 的結果是:
#有設 proxy
HTTP_USER_AGENT="Mozilla/5.0 (Windows; U; Windows NT 5.1; zh-TW; rv:1.8.1.11) Gecko/20071127 Firefox/2.0.0.11"
HTTP_VIA="1.1 xxx.xxx.tw:3128 (squid/2.5.STABLE11)"
HTTP_X_FORWARDED_FOR="3.4.5.6"(假冒宣稱代理IP)
REMOTE_ADDR="2.3.4.5"(proxy IP)
#沒設 proxy
HTTP_USER_AGENT="Mozilla/5.0 (Windows; U; Windows NT 5.1; zh-TW; rv:1.9.0.3) Gecko/2008092417 Firefox/3.0.3"
REMOTE_ADDR="1.2.3.4"(家裡IP)
看起來 REMOTE_ADDR 是在 perl cgi 裡可採用的環境變數。
RoR 的 HTTP 環境變數查詢
在 RoR 裡的 controller 中做一個 action
def server_ip
@httpenvs = request.env
end
而在相對的 views 中:
<ul>
<% @httpenvs.each do |k,v| -%>
<li><%= k -%> = <%= v -%></li>
<% end %>
</ul>
就可把 RoR 的 HTTP 環境變數列出。
觀察 RoR HTTP 環境變數
#有設 proxy
REMOTE_ADDR = 3.4.5.6(假冒宣稱代理IP), 2.3.4.5(proxy IP)
HTTP_X_FORWARDED_FOR = 3.4.5.6(假冒宣稱代理IP), 2.3.4.5(proxy IP)
HTTP_X_REAL_IP = 2.3.4.5(proxy IP)
#沒設 proxy
REMOTE_ADDR = 1.2.3.4(家裡IP)
HTTP_X_FORWARDED_FOR = 1.2.3.4(家裡IP)
HTTP_X_REAL_IP = 1.2.3.4(家裡IP)
從上述才瞭解到 HTTP_X_REAL_IP 才是與 RoR 該機器連線的 IP,不會有 proxy 所宣稱為哪個 IP 做連線代理。搞清楚這些變數的差別,才不致有門戶大開的情形。
squid 的設定
此語法是 squid 2.x 環境下可運作的語法:
# 若 off 則是設成 anonymous 的 proxy
forwarded_for on
header_access X-Forwarded-For deny all
header_replace X-Forwarded-For XXX.XXX.XXX.XXX
# 下面可設可不設,設的話,就會一律都以此秀出瀏覽器是那一個牌子的,也會讓 web server 端是該電腦本身直接連線。
header_access User-Agent deny all
header_replace User-Agent Mozilla/5.0 (Windows; U; Windows NT 5.1; zh-TW; rv:1.8.1.11) Gecko/20071127 Firefox/2.0.0.11
而現在最新作業系統卻是附 squid 3.x 版,語法有點不一樣:
forwarded_for on
request_header_access X-Forwarded-For deny all
request_header_access via deny all
request_header_access User-Agent deny all
header_replace X-Forwarded-For XXX.XXX.XXX.XXX
header_replace User-Agent Mozilla/5.0 (Windows; U; Windows NT 5.1; zh-TW; rv:1.8.1.11) Gecko/20071127 Firefox/2.0.0.11
設法是最近才試出來,運作是沒問題,但可能還需要做些調整。
所以結論是:不要相信 proxy 傳來的資訊。雖然從正面來看是保護 user 端的來源及使用環境。
除了可用上述的 perl 或 RoR 的 HTTP 環境變數的語法來測試外,常連到 http://ipid.shat.net 來看看變數是怎麼變化。