willcomの高速化サービスでIP spoofing attackエラー
問題
rails2.1のサイト(リバースプロクシ使用)をwillcomの高速化サービスのプロクシ(フロントプロクシ)を使うと
IP spoofing attack?!
というエラーになってしまいました。
(恐らくこのエラーを出すようになったのは2.1からっぽい。)
理由
ソースをおっていくとHTTPのリクエストヘッダーに
HTTP_CLIENT_IPとHTTP_X_FORWARDED_FORの
両方がある場合でかつIPがどちらも違う場合に起きるみたい。
(どちらもプロクシ経由時の要求元IPを返すヘッダー。リバプロ経由だとREMOTE_ADDRがリバプロのIPになるためこちらを参照する必要がある)
それで一体どっちのIPを使えばいいんだ、このインチキ野郎とエラーを出しているらしい。
恐らくHTTP_CLIENT_IPがフロントプロクシの要求元IPで
HTTP_X_FORWARDED_FORがリバースプロクシの要求元IPになっていると思われる。
HTTP_X_FORWARDED_FORの仕様として調べた限り
- 複数のプロクシを経由するとカンマ区切りで複数くる
- ただしリバースプロクシ(mod_proxy)を経由するとフロントプロクシで付与したHTTP_X_FORWARDED_FORは消される。
(そのようなドキュメントは見つからなかったのですがうちのリバプロではこんな挙動でした。
消えない動作の場合もあるみたいなのですが、何故消えるのかは不明。ver?)
そしてwillcomの仕様としては下記のどちらかと思われる
- HTTP_CLIENT_IPとHTTP_X_FORWARDED_FORの両方を送ってきている。(元のHTTP_X_FORWARDED_FORはリバプロで削除)
- HTTP_CLIENT_IPのみ送ってきている。(HTTP_X_FORWARDED_FORはリバースプロクシが付与)
このためHTTP_CLIENT_IPとHTTP_X_FORWARDED_FORが違くなると思われます。
同様の挙動をするプロクシを経由した場合でもこの問題が起きるでしょう。
HTTP_CLIENT_IPなんていうどこで改ざんされるかわからない信用できない値は使わなくてよいと思うのだけど
HTTP_CLIENT_IPを返すリバースプロクシがあった場合への対応かな?
そうだとすればモード切替があったほうがいいと思う。
でもってデフォルトはメジャーなHTTP_X_FORWARDED_FORみたいな。
解決策
- apacheレベルでClient-IPヘッダーを削除する。
- 下記の設定を追加?(試してはないです)
- RequestHeader unset Client-IP
- railsにモンキーパッチをあてる。
- config/enviroment.rb
module ActionController class AbstractRequest def remote_ip remote_addr_list = @env['REMOTE_ADDR'] && @env['REMOTE_ADDR'].split(',').collect(&:strip) unless remote_addr_list.blank? not_trusted_addrs = remote_addr_list.reject {|addr| addr =~ TRUSTED_PROXIES} return not_trusted_addrs.first unless not_trusted_addrs.empty? end remote_ips = @env['HTTP_X_FORWARDED_FOR'] && @env['HTTP_X_FORWARDED_FOR'].split(',') # HTTP_CLIENT_IPを返すリバースプロキシの場合はtrueにする。railsっぽいconf化したいけどわからない。。 if false if @env.include? 'HTTP_CLIENT_IP' return @env['HTTP_CLIENT_IP'] else raise ActionControllerError.new("no header. HTTP_CLIENT_IP.") end end if remote_ips while remote_ips.size > 1 && TRUSTED_PROXIES =~ remote_ips.last.strip remote_ips.pop end return remote_ips.last.strip end @env['REMOTE_ADDR'] end end end
remote_ipメソッドの挙動
ちなみにこのエラーを出しているremote_ipメソッドを眺めてて思ったのが
HTTP_CLIENT_IPやHTTP_X_FORWARDED_FORを
書き換えればIP偽装できんでねって疑問。
結論からいくと問題なさそうです。
最初にREMOTE_ADDRがローカルIPかどうかのチェックをし、
グローバルIPならそのまま返します。
リバースプロクシ経由の場合はREMOTE_ADDRにリバースプロクシのローカルIPが入るので
HTTP_X_FORWARDED_FORか、HTTP_CLIENT_IPを使います。
HTTP_X_FORWARDED_FORは複数になる可能性があるので
最後のIPがリバースプロクシに繋いできたIPになるため最後のIPを使用。
ただし多段のリバースプロクシの可能性もあるためお尻のローカルIPのものを削除してから。
元ソース
actionpack-2.1.2\lib\action_controller\request.rb
# Which IP addresses are "trusted proxies" that can be stripped from # the right-hand-side of X-Forwarded-For TRUSTED_PROXIES = /^127\.0\.0\.1$|^(10|172\.(1[6-9]|2[0-9]|30|31)|192\.168)\./i # Determine originating IP address. REMOTE_ADDR is the standard # but will fail if the user is behind a proxy. HTTP_CLIENT_IP and/or # HTTP_X_FORWARDED_FOR are set by proxies so check for these if # REMOTE_ADDR is a proxy. HTTP_X_FORWARDED_FOR may be a comma- # delimited list in the case of multiple chained proxies; the last # address which is not trusted is the originating IP. def remote_ip remote_addr_list = @env['REMOTE_ADDR'] && @env['REMOTE_ADDR'].split(',').collect(&:strip) unless remote_addr_list.blank? not_trusted_addrs = remote_addr_list.reject {|addr| addr =~ TRUSTED_PROXIES} return not_trusted_addrs.first unless not_trusted_addrs.empty? end remote_ips = @env['HTTP_X_FORWARDED_FOR'] && @env['HTTP_X_FORWARDED_FOR'].split(',') if @env.include? 'HTTP_CLIENT_IP' if remote_ips && !remote_ips.include?(@env['HTTP_CLIENT_IP']) # We don't know which came from the proxy, and which from the user raise ActionControllerError.new(<<EOM) IP spoofing attack?! HTTP_CLIENT_IP=#{@env['HTTP_CLIENT_IP'].inspect} HTTP_X_FORWARDED_FOR=#{@env['HTTP_X_FORWARDED_FOR'].inspect} EOM end return @env['HTTP_CLIENT_IP'] end if remote_ips while remote_ips.size > 1 && TRUSTED_PROXIES =~ remote_ips.last.strip remote_ips.pop end return remote_ips.last.strip end @env['REMOTE_ADDR'] end
参考HP
IP spoofing attack関連の情報(英語でよくわかってないです)
http://iprog.com/posting/2008/08/rails_500_error_ip_spoofing_attack
http://rails.lighthouseapp.com/projects/8994/tickets/322
プロクシのヘッダについて
http://www.nurs.or.jp/~sug/homep/proxy/proxy7.htm