Imports changes from Rails' revision r9125
| 1 | *SVN* | *SVN* | 1 |
|---|---|---|---|
| 2 | 2 | ||
* Avoid remote_ip spoofing. [Brian Candler] | 3 | ||
| 4 | |||
| 3 | * Correct inconsistencies in RequestForgeryProtection docs. #11032 [mislav] | * Correct inconsistencies in RequestForgeryProtection docs. #11032 [mislav] | 5 |
| 4 | 6 | ||
| 5 | * Make assert_routing aware of the HTTP method used. #8039 [mpalmer] | * Make assert_routing aware of the HTTP method used. #8039 [mpalmer] | 7 |
| 122 | end | end | 122 |
|---|---|---|---|
| 123 | alias xhr? :xml_http_request? | alias xhr? :xml_http_request? | 123 |
| 124 | 124 | ||
# Which IP addresses are "trusted proxies" that can be stripped from | 125 | ||
# the right-hand-side of X-Forwarded-For | 126 | ||
TRUSTED_PROXIES = /^127\.0\.0\.1$|^(10|172\.(1[6-9]|2[0-9]|30|31)|192\.168)\./i | 127 | ||
| 128 | |||
| 125 | # Determine originating IP address. REMOTE_ADDR is the standard | # Determine originating IP address. REMOTE_ADDR is the standard | 129 |
| 126 | # but will fail if the user is behind a proxy. HTTP_CLIENT_IP and/or | # but will fail if the user is behind a proxy. HTTP_CLIENT_IP and/or | 130 |
| 127 | # HTTP_X_FORWARDED_FOR are set by proxies so check for these before | # HTTP_X_FORWARDED_FOR are set by proxies so check for these if | 131 |
| 128 | # falling back to REMOTE_ADDR. HTTP_X_FORWARDED_FOR may be a comma- | # REMOTE_ADDR is a proxy. HTTP_X_FORWARDED_FOR may be a comma- | 132 |
| 129 | # delimited list in the case of multiple chained proxies; the first is | # delimited list in the case of multiple chained proxies; the last | 133 |
| 130 | # the originating IP. | # address which is not trusted is the originating IP. | 134 |
| 131 | # | 135 | |
| 132 | # Security note: do not use if IP spoofing is a concern for your | ||
| 133 | # application. Since remote_ip checks HTTP headers for addresses forwarded | ||
| 134 | # by proxies, the client may send any IP. remote_addr can't be spoofed but | ||
| 135 | # also doesn't work behind a proxy, since it's always the proxy's IP. | ||
| 136 | def remote_ip | def remote_ip | 136 |
| 137 | return @env['HTTP_CLIENT_IP'] if @env.include? 'HTTP_CLIENT_IP' | if TRUSTED_PROXIES !~ @env['REMOTE_ADDR'] | 137 |
return @env['REMOTE_ADDR'] | 138 | ||
end | 139 | ||
| 140 | |||
if @env.include? 'HTTP_CLIENT_IP' | 141 | ||
if @env.include? 'HTTP_X_FORWARDED_FOR' | 142 | ||
# We don't know which came from the proxy, and which from the user | 143 | ||
raise ActionControllerError.new(<<EOM) | 144 | ||
IP spoofing attack?! | 145 | ||
HTTP_CLIENT_IP=#{@env['HTTP_CLIENT_IP'].inspect} | 146 | ||
HTTP_X_FORWARDED_FOR=#{@env['HTTP_X_FORWARDED_FOR'].inspect} | 147 | ||
EOM | 148 | ||
end | 149 | ||
return @env['HTTP_CLIENT_IP'] | 150 | ||
end | 151 | ||
| 138 | 152 | ||
| 139 | if @env.include? 'HTTP_X_FORWARDED_FOR' then | if @env.include? 'HTTP_X_FORWARDED_FOR' then | 153 |
| 140 | remote_ips = @env['HTTP_X_FORWARDED_FOR'].split(',').reject do |ip| | remote_ips = @env['HTTP_X_FORWARDED_FOR'].split(',') | 154 |
| 141 | ip.strip =~ /^unknown$|^(10|172\.(1[6-9]|2[0-9]|30|31)|192\.168)\./i | while remote_ips.size > 1 && TRUSTED_PROXIES =~ remote_ips.last.strip | 155 |
remote_ips.pop | 156 | ||
| 142 | end | end | 157 |
| 143 | 158 | ||
| 144 | return remote_ips.first.strip unless remote_ips.empty? | return remote_ips.last.strip | 159 |
| 145 | end | end | 160 |
| 146 | 161 | ||
| 147 | @env['REMOTE_ADDR'] | @env['REMOTE_ADDR'] | 162 |
| 13 | assert_equal '1.2.3.4', @request.remote_ip | assert_equal '1.2.3.4', @request.remote_ip | 13 |
|---|---|---|---|
| 14 | 14 | ||
| 15 | @request.env['HTTP_CLIENT_IP'] = '2.3.4.5' | @request.env['HTTP_CLIENT_IP'] = '2.3.4.5' | 15 |
assert_equal '1.2.3.4', @request.remote_ip | 16 | ||
| 17 | |||
@request.remote_addr = '192.168.0.1' | 18 | ||
| 16 | assert_equal '2.3.4.5', @request.remote_ip | assert_equal '2.3.4.5', @request.remote_ip | 19 |
| 17 | @request.env.delete 'HTTP_CLIENT_IP' | @request.env.delete 'HTTP_CLIENT_IP' | 20 |
| 18 | 21 | ||
@request.remote_addr = '1.2.3.4' | 22 | ||
@request.env['HTTP_X_FORWARDED_FOR'] = '3.4.5.6' | 23 | ||
assert_equal '1.2.3.4', @request.remote_ip | 24 | ||
| 25 | |||
@request.remote_addr = '127.0.0.1' | 26 | ||
| 19 | @request.env['HTTP_X_FORWARDED_FOR'] = '3.4.5.6' | @request.env['HTTP_X_FORWARDED_FOR'] = '3.4.5.6' | 27 |
| 20 | assert_equal '3.4.5.6', @request.remote_ip | assert_equal '3.4.5.6', @request.remote_ip | 28 |
| 21 | 29 | ||
| 13 more lines | |||
| 35 | assert_equal '3.4.5.6', @request.remote_ip | assert_equal '3.4.5.6', @request.remote_ip | 43 |
| 36 | 44 | ||
| 37 | @request.env['HTTP_X_FORWARDED_FOR'] = '127.0.0.1,3.4.5.6' | @request.env['HTTP_X_FORWARDED_FOR'] = '127.0.0.1,3.4.5.6' | 45 |
| 38 | assert_equal '127.0.0.1', @request.remote_ip | assert_equal '3.4.5.6', @request.remote_ip | 46 |
| 39 | 47 | ||
| 40 | @request.env['HTTP_X_FORWARDED_FOR'] = 'unknown,192.168.0.1' | @request.env['HTTP_X_FORWARDED_FOR'] = 'unknown,192.168.0.1' | 48 |
| 41 | assert_equal '1.2.3.4', @request.remote_ip | assert_equal 'unknown', @request.remote_ip | 49 |
| 50 | |||
@request.env['HTTP_X_FORWARDED_FOR'] = '9.9.9.9, 3.4.5.6, 10.0.0.1, 172.31.4.4' | 51 | ||
assert_equal '3.4.5.6', @request.remote_ip | 52 | ||
| 53 | |||
@request.env['HTTP_CLIENT_IP'] = '8.8.8.8' | 54 | ||
e = assert_raises(ActionController::ActionControllerError) { | 55 | ||
@request.remote_ip | 56 | ||
} | 57 | ||
assert_match /IP spoofing attack/, e.message | 58 | ||
assert_match /HTTP_X_FORWARDED_FOR="9.9.9.9, 3.4.5.6, 10.0.0.1, 172.31.4.4"/, e.message | 59 | ||
assert_match /HTTP_CLIENT_IP="8.8.8.8"/, e.message | 60 | ||
| 61 | |||
@request.env.delete 'HTTP_CLIENT_IP' | 62 | ||
| 42 | @request.env.delete 'HTTP_X_FORWARDED_FOR' | @request.env.delete 'HTTP_X_FORWARDED_FOR' | 63 |
| 43 | end | end | 64 |
| 44 | 65 | ||