mirror of
https://github.com/rails/rails.git
synced 2022-11-09 12:12:34 -05:00
Restore original remote_ip algorithm.
Proxy servers add X-Forwarded-For headers, resulting in a list of IPs. We remove trusted IP values, and then take the last given value, assuming that it is the most likely to be the correct, unfaked value. See [1] for a very thorough discussion of why that is the best option we have at the moment. [1]: http://blog.gingerlime.com/2012/rails-ip-spoofing-vulnerabilities-and-protection/ Fixes #7979
This commit is contained in:
parent
9f200fb296
commit
75dcdbc84e
5 changed files with 165 additions and 112 deletions
|
@ -1,5 +1,13 @@
|
|||
## Rails 4.0.0 (unreleased) ##
|
||||
|
||||
* Return the last valid, non-private IP address from the X-Forwarded-For,
|
||||
Client-IP and Remote-Addr headers, in that order. Document the rationale
|
||||
for that decision, and describe the options that can be passed to the
|
||||
RemoteIp middleware to change it.
|
||||
Fix #7979
|
||||
|
||||
*André Arko*, *Steve Klabnik*, *Alexey Gaziev*
|
||||
|
||||
* Do not append second slash to `root_url` when using `trailing_slash: true`
|
||||
Fix #8700
|
||||
|
||||
|
|
|
@ -1,23 +1,58 @@
|
|||
module ActionDispatch
|
||||
# This middleware calculates the IP address of the remote client that is
|
||||
# making the request. It does this by checking various headers that could
|
||||
# contain the address, and then picking the last-set address that is not
|
||||
# on the list of trusted IPs. This follows the precendent set by e.g.
|
||||
# {the Tomcat server}[https://issues.apache.org/bugzilla/show_bug.cgi?id=50453],
|
||||
# with {reasoning explained at length}[http://blog.gingerlime.com/2012/rails-ip-spoofing-vulnerabilities-and-protection]
|
||||
# by @gingerlime. A more detailed explanation of the algorithm is given
|
||||
# at GetIp#calculate_ip.
|
||||
#
|
||||
# Some Rack servers concatenate repeated headers, like {HTTP RFC 2616}[http://www.w3.org/Protocols/rfc2616/rfc2616-sec4.html#sec4.2]
|
||||
# requires. Some Rack servers simply drop preceeding headers, and only report
|
||||
# the value that was {given in the last header}[http://andre.arko.net/2011/12/26/repeated-headers-and-ruby-web-servers].
|
||||
# If you are behind multiple proxy servers (like Nginx to HAProxy to Unicorn)
|
||||
# then you should test your Rack server to make sure your data is good.
|
||||
#
|
||||
# IF YOU DON'T USE A PROXY, THIS MAKES YOU VULNERABLE TO IP SPOOFING.
|
||||
# This middleware assumes that there is at least one proxy sitting around
|
||||
# and setting headers with the client's remote IP address. If you don't use
|
||||
# a proxy, because you are hosted on e.g. Heroku, any client can claim to
|
||||
# have any IP address by setting the X-Forwarded-For header. If you care
|
||||
# about that, please take precautions.
|
||||
class RemoteIp
|
||||
class IpSpoofAttackError < StandardError ; end
|
||||
class IpSpoofAttackError < StandardError; end
|
||||
|
||||
# IP addresses that are "trusted proxies" that can be stripped from
|
||||
# the comma-delimited list in the X-Forwarded-For header. See also:
|
||||
# http://en.wikipedia.org/wiki/Private_network#Private_IPv4_address_spaces
|
||||
# http://en.wikipedia.org/wiki/Private_network#Private_IPv6_addresses.
|
||||
# The default trusted IPs list simply includes IP addresses that are
|
||||
# guaranteed by the IP specification to be private addresses. Those will
|
||||
# not be the ultimate client IP in production, and so are discarded. See
|
||||
# http://en.wikipedia.org/wiki/Private_network for details.
|
||||
TRUSTED_PROXIES = %r{
|
||||
^127\.0\.0\.1$ | # localhost
|
||||
^::1$ |
|
||||
^(10 | # private IP 10.x.x.x
|
||||
172\.(1[6-9]|2[0-9]|3[0-1]) | # private IP in the range 172.16.0.0 .. 172.31.255.255
|
||||
192\.168 | # private IP 192.168.x.x
|
||||
fc00:: # private IP fc00
|
||||
)\.
|
||||
^127\.0\.0\.1$ | # localhost IPv4
|
||||
^::1$ | # localhost IPv6
|
||||
^fc00: | # private IPv6 range fc00
|
||||
^10\. | # private IPv4 range 10.x.x.x
|
||||
^172\.(1[6-9]|2[0-9]|3[0-1])\.| # private IPv4 range 172.16.0.0 .. 172.31.255.255
|
||||
^192\.168\. # private IPv4 range 192.168.x.x
|
||||
}x
|
||||
|
||||
attr_reader :check_ip, :proxies
|
||||
|
||||
# Create a new +RemoteIp+ middleware instance.
|
||||
#
|
||||
# The +check_ip_spoofing+ option is on by default. When on, an exception
|
||||
# is raised if it looks like the client is trying to lie about its own IP
|
||||
# address. It makes sense to turn off this check on sites aimed at non-IP
|
||||
# clients (like WAP devices), or behind proxies that set headers in an
|
||||
# incorrect or confusing way (like AWS ELB).
|
||||
#
|
||||
# The +custom_trusted+ argument can take a regex, which will be used
|
||||
# instead of +TRUSTED_PROXIES+, or a string, which will be used in addition
|
||||
# to +TRUSTED_PROXIES+. Any proxy setup will put the value you want in the
|
||||
# middle (or at the beginning) of the X-Forwarded-For list, with your proxy
|
||||
# servers after it. If your proxies aren't removed, pass them in via the
|
||||
# +custom_trusted+ parameter. That way, the middleware will ignore those
|
||||
# IP addresses, and return the one that you want.
|
||||
def initialize(app, check_ip_spoofing = true, custom_proxies = nil)
|
||||
@app = app
|
||||
@check_ip = check_ip_spoofing
|
||||
|
@ -31,15 +66,23 @@ module ActionDispatch
|
|||
end
|
||||
end
|
||||
|
||||
# Since the IP address may not be needed, we store the object here
|
||||
# without calculating the IP to keep from slowing down the majority of
|
||||
# requests. For those requests that do need to know the IP, the
|
||||
# GetIp#calculate_ip method will calculate the memoized client IP address.
|
||||
def call(env)
|
||||
env["action_dispatch.remote_ip"] = GetIp.new(env, self)
|
||||
@app.call(env)
|
||||
end
|
||||
|
||||
# The GetIp class exists as a way to defer processing of the request data
|
||||
# into an actual IP address. If the ActionDispatch::Request#remote_ip method
|
||||
# is called, this class will calculate the value and then memoize it.
|
||||
class GetIp
|
||||
|
||||
# IP v4 and v6 (with compression) validation regexp
|
||||
# https://gist.github.com/1289635
|
||||
# This constant contains a regular expression that validates every known
|
||||
# form of IP v4 and v6 address, with or without abbreviations, adapted
|
||||
# from {this gist}[https://gist.github.com/1289635].
|
||||
VALID_IP = %r{
|
||||
(^(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[0-9]{1,2})(\.(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[0-9]{1,2})){3}$) | # ip v4
|
||||
(^(
|
||||
|
@ -63,62 +106,78 @@ module ActionDispatch
|
|||
}x
|
||||
|
||||
def initialize(env, middleware)
|
||||
@env = env
|
||||
@middleware = middleware
|
||||
@ip = nil
|
||||
@env = env
|
||||
@check_ip = middleware.check_ip
|
||||
@proxies = middleware.proxies
|
||||
end
|
||||
|
||||
# Determines originating IP address. REMOTE_ADDR is the standard
|
||||
# but will be wrong if the user is behind a proxy. Proxies will set
|
||||
# HTTP_CLIENT_IP and/or HTTP_X_FORWARDED_FOR, so we prioritize those.
|
||||
# HTTP_X_FORWARDED_FOR may be a comma-delimited list in the case of
|
||||
# multiple chained proxies. The first address which is in this list
|
||||
# if it's not a known proxy will be the originating IP.
|
||||
# Format of HTTP_X_FORWARDED_FOR:
|
||||
# client_ip, proxy_ip1, proxy_ip2...
|
||||
# http://en.wikipedia.org/wiki/X-Forwarded-For
|
||||
# Sort through the various IP address headers, looking for the IP most
|
||||
# likely to be the address of the actual remote client making this
|
||||
# request.
|
||||
#
|
||||
# REMOTE_ADDR will be correct if the request is made directly against the
|
||||
# Ruby process, on e.g. Heroku. When the request is proxied by another
|
||||
# server like HAProxy or Nginx, the IP address that made the original
|
||||
# request will be put in an X-Forwarded-For header. If there are multiple
|
||||
# proxies, that header may contain a list of IPs. Other proxy services
|
||||
# set the Client-Ip header instead, so we check that too.
|
||||
#
|
||||
# As discussed in {this post about Rails IP Spoofing}[http://blog.gingerlime.com/2012/rails-ip-spoofing-vulnerabilities-and-protection/],
|
||||
# while the first IP in the list is likely to be the "originating" IP,
|
||||
# it could also have been set by the client maliciously.
|
||||
#
|
||||
# In order to find the first address that is (probably) accurate, we
|
||||
# take the list of IPs, remove known and trusted proxies, and then take
|
||||
# the last address left, which was presumably set by one of those proxies.
|
||||
def calculate_ip
|
||||
client_ip = @env['HTTP_CLIENT_IP']
|
||||
forwarded_ip = ips_from('HTTP_X_FORWARDED_FOR').first
|
||||
remote_addrs = ips_from('REMOTE_ADDR')
|
||||
# Set by the Rack web server, this is a single value.
|
||||
remote_addr = ips_from('REMOTE_ADDR').last
|
||||
|
||||
check_ip = client_ip && @middleware.check_ip
|
||||
if check_ip && forwarded_ip != client_ip
|
||||
# Could be a CSV list and/or repeated headers that were concatenated.
|
||||
client_ips = ips_from('HTTP_CLIENT_IP').reverse
|
||||
forwarded_ips = ips_from('HTTP_X_FORWARDED_FOR').reverse
|
||||
|
||||
# +Client-Ip+ and +X-Forwarded-For+ should not, generally, both be set.
|
||||
# If they are both set, it means that this request passed through two
|
||||
# proxies with incompatible IP header conventions, and there is no way
|
||||
# for us to determine which header is the right one after the fact.
|
||||
# Since we have no idea, we give up and explode.
|
||||
should_check_ip = @check_ip && client_ips.last
|
||||
if should_check_ip && !forwarded_ips.include?(client_ips.last)
|
||||
# We don't know which came from the proxy, and which from the user
|
||||
raise IpSpoofAttackError, "IP spoofing attack?!" \
|
||||
"HTTP_CLIENT_IP=#{@env['HTTP_CLIENT_IP'].inspect}" \
|
||||
raise IpSpoofAttackError, "IP spoofing attack?! " +
|
||||
"HTTP_CLIENT_IP=#{@env['HTTP_CLIENT_IP'].inspect} " +
|
||||
"HTTP_X_FORWARDED_FOR=#{@env['HTTP_X_FORWARDED_FOR'].inspect}"
|
||||
end
|
||||
|
||||
client_ips = remove_proxies [client_ip, forwarded_ip, remote_addrs].flatten
|
||||
if client_ips.present?
|
||||
client_ips.first
|
||||
else
|
||||
# If there is no client ip we can return first valid proxy ip from REMOTE_ADDR
|
||||
remote_addrs.find { |ip| valid_ip? ip }
|
||||
end
|
||||
# We assume these things about the IP headers:
|
||||
#
|
||||
# - X-Forwarded-For will be a list of IPs, one per proxy, or blank
|
||||
# - Client-Ip is propagated from the outermost proxy, or is blank
|
||||
# - REMOTE_ADDR will be the IP that made the request to Rack
|
||||
ips = [forwarded_ips, client_ips, remote_addr].flatten.compact
|
||||
|
||||
# If every single IP option is in the trusted list, just return REMOTE_ADDR
|
||||
filter_proxies(ips).first || remote_addr
|
||||
end
|
||||
|
||||
# Memoizes the value returned by #calculate_ip and returns it for
|
||||
# ActionDispatch::Request to use.
|
||||
def to_s
|
||||
@ip ||= calculate_ip
|
||||
end
|
||||
|
||||
private
|
||||
protected
|
||||
|
||||
def ips_from(header)
|
||||
@env[header] ? @env[header].strip.split(/[,\s]+/) : []
|
||||
# Split the comma-separated list into an array of strings
|
||||
ips = @env[header] ? @env[header].strip.split(/[,\s]+/) : []
|
||||
# Only return IPs that are valid according to the regex
|
||||
ips.select{ |ip| ip =~ VALID_IP }
|
||||
end
|
||||
|
||||
def valid_ip?(ip)
|
||||
ip =~ VALID_IP
|
||||
end
|
||||
|
||||
def not_a_proxy?(ip)
|
||||
ip !~ @middleware.proxies
|
||||
end
|
||||
|
||||
def remove_proxies(ips)
|
||||
ips.select { |ip| valid_ip?(ip) && not_a_proxy?(ip) }
|
||||
def filter_proxies(ips)
|
||||
ips.reject { |ip| ip =~ @proxies }
|
||||
end
|
||||
|
||||
end
|
||||
|
|
|
@ -34,7 +34,7 @@ class RequestTest < ActiveSupport::TestCase
|
|||
assert_equal '1.2.3.4', request.remote_ip
|
||||
|
||||
request = stub_request 'REMOTE_ADDR' => '1.2.3.4,3.4.5.6'
|
||||
assert_equal '1.2.3.4', request.remote_ip
|
||||
assert_equal '3.4.5.6', request.remote_ip
|
||||
|
||||
request = stub_request 'REMOTE_ADDR' => '1.2.3.4',
|
||||
'HTTP_X_FORWARDED_FOR' => '3.4.5.6'
|
||||
|
@ -47,30 +47,32 @@ class RequestTest < ActiveSupport::TestCase
|
|||
request = stub_request 'HTTP_X_FORWARDED_FOR' => '3.4.5.6,unknown'
|
||||
assert_equal '3.4.5.6', request.remote_ip
|
||||
|
||||
request = stub_request 'HTTP_X_FORWARDED_FOR' => '172.16.0.1,3.4.5.6'
|
||||
assert_equal nil, request.remote_ip
|
||||
request = stub_request 'HTTP_X_FORWARDED_FOR' => '3.4.5.6,172.16.0.1'
|
||||
assert_equal '3.4.5.6', request.remote_ip
|
||||
|
||||
request = stub_request 'HTTP_X_FORWARDED_FOR' => '192.168.0.1,3.4.5.6'
|
||||
assert_equal nil, request.remote_ip
|
||||
request = stub_request 'HTTP_X_FORWARDED_FOR' => '3.4.5.6,192.168.0.1'
|
||||
assert_equal '3.4.5.6', request.remote_ip
|
||||
|
||||
request = stub_request 'HTTP_X_FORWARDED_FOR' => '10.0.0.1,3.4.5.6'
|
||||
assert_equal nil, request.remote_ip
|
||||
request = stub_request 'HTTP_X_FORWARDED_FOR' => '3.4.5.6,10.0.0.1'
|
||||
assert_equal '3.4.5.6', request.remote_ip
|
||||
|
||||
request = stub_request 'HTTP_X_FORWARDED_FOR' => '10.0.0.1, 10.0.0.1, 3.4.5.6'
|
||||
assert_equal nil, request.remote_ip
|
||||
request = stub_request 'HTTP_X_FORWARDED_FOR' => '3.4.5.6, 10.0.0.1, 10.0.0.1'
|
||||
assert_equal '3.4.5.6', request.remote_ip
|
||||
|
||||
request = stub_request 'HTTP_X_FORWARDED_FOR' => '127.0.0.1,3.4.5.6'
|
||||
assert_equal nil, request.remote_ip
|
||||
request = stub_request 'HTTP_X_FORWARDED_FOR' => '3.4.5.6,127.0.0.1'
|
||||
assert_equal '3.4.5.6', request.remote_ip
|
||||
|
||||
request = stub_request 'HTTP_X_FORWARDED_FOR' => 'unknown,192.168.0.1'
|
||||
assert_equal nil, request.remote_ip
|
||||
|
||||
request = stub_request 'HTTP_X_FORWARDED_FOR' => '3.4.5.6, 9.9.9.9, 10.0.0.1, 172.31.4.4'
|
||||
request = stub_request 'HTTP_X_FORWARDED_FOR' => '9.9.9.9, 3.4.5.6, 172.31.4.4, 10.0.0.1'
|
||||
assert_equal '3.4.5.6', request.remote_ip
|
||||
|
||||
request = stub_request 'HTTP_X_FORWARDED_FOR' => 'not_ip_address'
|
||||
assert_equal nil, request.remote_ip
|
||||
end
|
||||
|
||||
test "remote ip spoof detection" do
|
||||
request = stub_request 'HTTP_X_FORWARDED_FOR' => '1.1.1.1',
|
||||
'HTTP_CLIENT_IP' => '2.2.2.2'
|
||||
e = assert_raise(ActionDispatch::RemoteIp::IpSpoofAttackError) {
|
||||
|
@ -79,26 +81,20 @@ class RequestTest < ActiveSupport::TestCase
|
|||
assert_match(/IP spoofing attack/, e.message)
|
||||
assert_match(/HTTP_X_FORWARDED_FOR="1.1.1.1"/, e.message)
|
||||
assert_match(/HTTP_CLIENT_IP="2.2.2.2"/, e.message)
|
||||
end
|
||||
|
||||
# turn IP Spoofing detection off.
|
||||
# This is useful for sites that are aimed at non-IP clients. The typical
|
||||
# example is WAP. Since the cellular network is not IP based, it's a
|
||||
# leap of faith to assume that their proxies are ever going to set the
|
||||
# HTTP_CLIENT_IP/HTTP_X_FORWARDED_FOR headers properly.
|
||||
test "remote ip with spoof detection disabled" do
|
||||
request = stub_request 'HTTP_X_FORWARDED_FOR' => '1.1.1.1',
|
||||
'HTTP_CLIENT_IP' => '2.2.2.2',
|
||||
:ip_spoofing_check => false
|
||||
assert_equal '2.2.2.2', request.remote_ip
|
||||
|
||||
request = stub_request 'HTTP_X_FORWARDED_FOR' => '9.9.9.9, 8.8.8.8'
|
||||
assert_equal '9.9.9.9', request.remote_ip
|
||||
assert_equal '1.1.1.1', request.remote_ip
|
||||
end
|
||||
|
||||
test "remote ip v6" do
|
||||
request = stub_request 'REMOTE_ADDR' => '2001:0db8:85a3:0000:0000:8a2e:0370:7334'
|
||||
assert_equal '2001:0db8:85a3:0000:0000:8a2e:0370:7334', request.remote_ip
|
||||
|
||||
request = stub_request 'REMOTE_ADDR' => '2001:0db8:85a3:0000:0000:8a2e:0370:7334,fe80:0000:0000:0000:0202:b3ff:fe1e:8329'
|
||||
request = stub_request 'REMOTE_ADDR' => 'fe80:0000:0000:0000:0202:b3ff:fe1e:8329,2001:0db8:85a3:0000:0000:8a2e:0370:7334'
|
||||
assert_equal '2001:0db8:85a3:0000:0000:8a2e:0370:7334', request.remote_ip
|
||||
|
||||
request = stub_request 'REMOTE_ADDR' => '2001:0db8:85a3:0000:0000:8a2e:0370:7334',
|
||||
|
@ -109,30 +105,26 @@ class RequestTest < ActiveSupport::TestCase
|
|||
'HTTP_X_FORWARDED_FOR' => 'fe80:0000:0000:0000:0202:b3ff:fe1e:8329'
|
||||
assert_equal 'fe80:0000:0000:0000:0202:b3ff:fe1e:8329', request.remote_ip
|
||||
|
||||
request = stub_request 'HTTP_X_FORWARDED_FOR' => 'unknown,fe80:0000:0000:0000:0202:b3ff:fe1e:8329'
|
||||
assert_equal nil, request.remote_ip
|
||||
request = stub_request 'HTTP_X_FORWARDED_FOR' => 'fe80:0000:0000:0000:0202:b3ff:fe1e:8329,unknown'
|
||||
assert_equal 'fe80:0000:0000:0000:0202:b3ff:fe1e:8329', request.remote_ip
|
||||
|
||||
request = stub_request 'HTTP_X_FORWARDED_FOR' => '::1,fe80:0000:0000:0000:0202:b3ff:fe1e:8329'
|
||||
assert_equal nil, request.remote_ip
|
||||
request = stub_request 'HTTP_X_FORWARDED_FOR' => 'fe80:0000:0000:0000:0202:b3ff:fe1e:8329,::1'
|
||||
assert_equal 'fe80:0000:0000:0000:0202:b3ff:fe1e:8329', request.remote_ip
|
||||
|
||||
request = stub_request 'HTTP_X_FORWARDED_FOR' => '::1,fe80:0000:0000:0000:0202:b3ff:fe1e:8329'
|
||||
assert_equal nil, request.remote_ip
|
||||
|
||||
request = stub_request 'HTTP_X_FORWARDED_FOR' => '::1,fe80:0000:0000:0000:0202:b3ff:fe1e:8329'
|
||||
assert_equal nil, request.remote_ip
|
||||
|
||||
request = stub_request 'HTTP_X_FORWARDED_FOR' => '::1, ::1, fe80:0000:0000:0000:0202:b3ff:fe1e:8329'
|
||||
assert_equal nil, request.remote_ip
|
||||
request = stub_request 'HTTP_X_FORWARDED_FOR' => 'fe80:0000:0000:0000:0202:b3ff:fe1e:8329, ::1, ::1'
|
||||
assert_equal 'fe80:0000:0000:0000:0202:b3ff:fe1e:8329', request.remote_ip
|
||||
|
||||
request = stub_request 'HTTP_X_FORWARDED_FOR' => 'unknown,::1'
|
||||
assert_equal nil, request.remote_ip
|
||||
|
||||
request = stub_request 'HTTP_X_FORWARDED_FOR' => '2001:0db8:85a3:0000:0000:8a2e:0370:7334, fe80:0000:0000:0000:0202:b3ff:fe1e:8329, ::1, fc00::'
|
||||
assert_equal '2001:0db8:85a3:0000:0000:8a2e:0370:7334', request.remote_ip
|
||||
assert_equal 'fe80:0000:0000:0000:0202:b3ff:fe1e:8329', request.remote_ip
|
||||
|
||||
request = stub_request 'HTTP_X_FORWARDED_FOR' => 'not_ip_address'
|
||||
assert_equal nil, request.remote_ip
|
||||
end
|
||||
|
||||
test "remote ip v6 spoof detection" do
|
||||
request = stub_request 'HTTP_X_FORWARDED_FOR' => 'fe80:0000:0000:0000:0202:b3ff:fe1e:8329',
|
||||
'HTTP_CLIENT_IP' => '2001:0db8:85a3:0000:0000:8a2e:0370:7334'
|
||||
e = assert_raise(ActionDispatch::RemoteIp::IpSpoofAttackError) {
|
||||
|
@ -141,26 +133,15 @@ class RequestTest < ActiveSupport::TestCase
|
|||
assert_match(/IP spoofing attack/, e.message)
|
||||
assert_match(/HTTP_X_FORWARDED_FOR="fe80:0000:0000:0000:0202:b3ff:fe1e:8329"/, e.message)
|
||||
assert_match(/HTTP_CLIENT_IP="2001:0db8:85a3:0000:0000:8a2e:0370:7334"/, e.message)
|
||||
end
|
||||
|
||||
# Turn IP Spoofing detection off.
|
||||
# This is useful for sites that are aimed at non-IP clients. The typical
|
||||
# example is WAP. Since the cellular network is not IP based, it's a
|
||||
# leap of faith to assume that their proxies are ever going to set the
|
||||
# HTTP_CLIENT_IP/HTTP_X_FORWARDED_FOR headers properly.
|
||||
test "remote ip v6 spoof detection disabled" do
|
||||
request = stub_request 'HTTP_X_FORWARDED_FOR' => 'fe80:0000:0000:0000:0202:b3ff:fe1e:8329',
|
||||
'HTTP_CLIENT_IP' => '2001:0db8:85a3:0000:0000:8a2e:0370:7334',
|
||||
:ip_spoofing_check => false
|
||||
assert_equal '2001:0db8:85a3:0000:0000:8a2e:0370:7334', request.remote_ip
|
||||
|
||||
request = stub_request 'HTTP_X_FORWARDED_FOR' => 'fe80:0000:0000:0000:0202:b3ff:fe1e:8329, 2001:0db8:85a3:0000:0000:8a2e:0370:7334'
|
||||
assert_equal 'fe80:0000:0000:0000:0202:b3ff:fe1e:8329', request.remote_ip
|
||||
end
|
||||
|
||||
test "remote ip when the remote ip middleware returns nil" do
|
||||
request = stub_request 'REMOTE_ADDR' => '127.0.0.1'
|
||||
assert_equal '127.0.0.1', request.remote_ip
|
||||
end
|
||||
|
||||
test "remote ip with user specified trusted proxies String" do
|
||||
@trusted_proxies = "67.205.106.73"
|
||||
|
||||
|
@ -170,16 +151,16 @@ class RequestTest < ActiveSupport::TestCase
|
|||
|
||||
request = stub_request 'REMOTE_ADDR' => '172.16.0.1,67.205.106.73',
|
||||
'HTTP_X_FORWARDED_FOR' => '67.205.106.73'
|
||||
assert_equal '172.16.0.1', request.remote_ip
|
||||
assert_equal '67.205.106.73', request.remote_ip
|
||||
|
||||
request = stub_request 'REMOTE_ADDR' => '67.205.106.73,3.4.5.6',
|
||||
'HTTP_X_FORWARDED_FOR' => '67.205.106.73'
|
||||
assert_equal '3.4.5.6', request.remote_ip
|
||||
|
||||
request = stub_request 'HTTP_X_FORWARDED_FOR' => 'unknown,67.205.106.73'
|
||||
request = stub_request 'HTTP_X_FORWARDED_FOR' => '67.205.106.73,unknown'
|
||||
assert_equal nil, request.remote_ip
|
||||
|
||||
request = stub_request 'HTTP_X_FORWARDED_FOR' => '3.4.5.6, 9.9.9.9, 10.0.0.1, 67.205.106.73'
|
||||
request = stub_request 'HTTP_X_FORWARDED_FOR' => '9.9.9.9, 3.4.5.6, 10.0.0.1, 67.205.106.73'
|
||||
assert_equal '3.4.5.6', request.remote_ip
|
||||
end
|
||||
|
||||
|
@ -196,13 +177,13 @@ class RequestTest < ActiveSupport::TestCase
|
|||
|
||||
request = stub_request 'REMOTE_ADDR' => 'fe80:0000:0000:0000:0202:b3ff:fe1e:8329,::1',
|
||||
'HTTP_X_FORWARDED_FOR' => 'fe80:0000:0000:0000:0202:b3ff:fe1e:8329'
|
||||
assert_equal 'fe80:0000:0000:0000:0202:b3ff:fe1e:8329', request.remote_ip
|
||||
assert_equal '::1', request.remote_ip
|
||||
|
||||
request = stub_request 'HTTP_X_FORWARDED_FOR' => 'unknown,fe80:0000:0000:0000:0202:b3ff:fe1e:8329'
|
||||
assert_equal nil, request.remote_ip
|
||||
|
||||
request = stub_request 'HTTP_X_FORWARDED_FOR' => 'fe80:0000:0000:0000:0202:b3ff:fe1e:8329,2001:0db8:85a3:0000:0000:8a2e:0370:7334'
|
||||
assert_equal nil, request.remote_ip
|
||||
assert_equal "2001:0db8:85a3:0000:0000:8a2e:0370:7334", request.remote_ip
|
||||
end
|
||||
|
||||
test "remote ip with user specified trusted proxies Regexp" do
|
||||
|
@ -212,8 +193,8 @@ class RequestTest < ActiveSupport::TestCase
|
|||
'HTTP_X_FORWARDED_FOR' => '3.4.5.6'
|
||||
assert_equal '3.4.5.6', request.remote_ip
|
||||
|
||||
request = stub_request 'HTTP_X_FORWARDED_FOR' => '67.205.106.73, 10.0.0.1, 9.9.9.9, 3.4.5.6'
|
||||
assert_equal nil, request.remote_ip
|
||||
request = stub_request 'HTTP_X_FORWARDED_FOR' => '10.0.0.1, 9.9.9.9, 3.4.5.6, 67.205.106.73'
|
||||
assert_equal '3.4.5.6', request.remote_ip
|
||||
end
|
||||
|
||||
test "remote ip v6 with user specified trusted proxies Regexp" do
|
||||
|
@ -223,8 +204,13 @@ class RequestTest < ActiveSupport::TestCase
|
|||
'HTTP_X_FORWARDED_FOR' => 'fe80:0000:0000:0000:0202:b3ff:fe1e:8329'
|
||||
assert_equal '2001:0db8:85a3:0000:0000:8a2e:0370:7334', request.remote_ip
|
||||
|
||||
request = stub_request 'HTTP_X_FORWARDED_FOR' => 'fe80:0000:0000:0000:0202:b3ff:fe1e:8329, 2001:0db8:85a3:0000:0000:8a2e:0370:7334'
|
||||
assert_equal nil, request.remote_ip
|
||||
request = stub_request 'HTTP_X_FORWARDED_FOR' => '2001:0db8:85a3:0000:0000:8a2e:0370:7334, fe80:0000:0000:0000:0202:b3ff:fe1e:8329'
|
||||
assert_equal '2001:0db8:85a3:0000:0000:8a2e:0370:7334', request.remote_ip
|
||||
end
|
||||
|
||||
test "remote ip middleware not present still returns an IP" do
|
||||
request = ActionDispatch::Request.new({'REMOTE_ADDR' => '127.0.0.1'})
|
||||
assert_equal '127.0.0.1', request.remote_ip
|
||||
end
|
||||
|
||||
test "domains" do
|
||||
|
@ -602,7 +588,7 @@ class RequestTest < ActiveSupport::TestCase
|
|||
assert_equal request.format.xml?, false
|
||||
assert_equal request.format.json?, false
|
||||
end
|
||||
|
||||
|
||||
test "formats with xhr request" do
|
||||
request = stub_request 'HTTP_X_REQUESTED_WITH' => "XMLHttpRequest"
|
||||
request.expects(:parameters).at_least_once.returns({})
|
||||
|
|
|
@ -197,7 +197,7 @@ Every Rails application comes with a standard set of middleware which it uses in
|
|||
* `Rails::Rack::Logger` notifies the logs that the request has began. After request is complete, flushes all the logs.
|
||||
* `ActionDispatch::ShowExceptions` rescues any exception returned by the application and renders nice exception pages if the request is local or if `config.consider_all_requests_local` is set to `true`. If `config.action_dispatch.show_exceptions` is set to `false`, exceptions will be raised regardless.
|
||||
* `ActionDispatch::RequestId` makes a unique X-Request-Id header available to the response and enables the `ActionDispatch::Request#uuid` method.
|
||||
* `ActionDispatch::RemoteIp` checks for IP spoofing attacks. Configurable with the `config.action_dispatch.ip_spoofing_check` and `config.action_dispatch.trusted_proxies` settings.
|
||||
* `ActionDispatch::RemoteIp` checks for IP spoofing attacks and gets valid `client_ip` from request headers. Configurable with the `config.action_dispatch.ip_spoofing_check`, and `config.action_dispatch.trusted_proxies` options.
|
||||
* `Rack::Sendfile` intercepts responses whose body is being served from a file and replaces it with a server specific X-Sendfile header. Configurable with `config.action_dispatch.x_sendfile_header`.
|
||||
* `ActionDispatch::Callbacks` runs the prepare callbacks before serving the request.
|
||||
* `ActiveRecord::ConnectionAdapters::ConnectionManagement` cleans active connections after each request, unless the `rack.test` key in the request environment is set to `true`.
|
||||
|
|
|
@ -40,7 +40,7 @@ module ApplicationTests
|
|||
end
|
||||
|
||||
assert_nothing_raised(ActionDispatch::RemoteIp::IpSpoofAttackError) do
|
||||
assert_equal "1.1.1.2", remote_ip("HTTP_X_FORWARDED_FOR" => "1.1.1.1", "HTTP_CLIENT_IP" => "1.1.1.2")
|
||||
assert_equal "1.1.1.1", remote_ip("HTTP_X_FORWARDED_FOR" => "1.1.1.1", "HTTP_CLIENT_IP" => "1.1.1.2")
|
||||
end
|
||||
end
|
||||
|
||||
|
|
Loading…
Reference in a new issue