Module: Geocoder::Request

Defined in:
lib/geocoder/request.rb

Constant Summary collapse

GEOCODER_CANDIDATE_HEADERS =

There's a whole zoo of nonstandard headers added by various

proxy softwares to indicate original client IP.

ANY of these can be trivially spoofed!

(except REMOTE_ADDR, which should by set by your server,
 and is included at the end as a fallback.

Order does matter: we're following the convention established in

ActionDispatch::RemoteIp::GetIp::calculate_ip()
https://github.com/rails/rails/blob/master/actionpack/lib/action_dispatch/middleware/remote_ip.rb
where the forwarded_for headers, possibly containing lists,
are arbitrarily preferred over headers expected to contain a
single address.
['HTTP_X_FORWARDED_FOR',
'HTTP_X_FORWARDED',
'HTTP_FORWARDED_FOR',
'HTTP_FORWARDED',
'HTTP_X_CLIENT_IP',
'HTTP_CLIENT_IP',
'HTTP_X_REAL_IP',
'HTTP_X_CLUSTER_CLIENT_IP',
'REMOTE_ADDR']

Instance Method Summary collapse

Instance Method Details

#geocoder_spoofable_ipObject


46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
# File 'lib/geocoder/request.rb', line 46

def geocoder_spoofable_ip

  # We could use a more sophisticated IP-guessing algorithm here,
  # in which we'd try to resolve the use of different headers by
  # different proxies.  The idea is that by comparing IPs repeated
  # in different headers, you can sometimes decide which header
  # was used by a proxy further along in the chain, and thus
  # prefer the headers used earlier.  However, the gains might not
  # be worth the performance tradeoff, since this method is likely
  # to be called on every request in a lot of applications.
  GEOCODER_CANDIDATE_HEADERS.each do |header|
    if @env.has_key? header
      addrs = geocoder_split_ip_addresses(@env[header])
      addrs = geocoder_remove_port_from_addresses(addrs)
      addrs = geocoder_reject_non_ipv4_addresses(addrs)
      addrs = geocoder_reject_trusted_ip_addresses(addrs)
      return addrs.first if addrs.any?
    end
  end

  @env['REMOTE_ADDR']
end

#locationObject

The location() method is vulnerable to trivial IP spoofing.

Don't use it in authorization/authentication code, or any
other security-sensitive application.  Use safe_location
instead.

10
11
12
# File 'lib/geocoder/request.rb', line 10

def location
  @location ||= Geocoder.search(geocoder_spoofable_ip, ip_address: true).first
end

#safe_locationObject

This safe_location() protects you from trivial IP spoofing.

For requests that go through a proxy that you haven't
whitelisted as trusted in your Rack config, you will get the
location for the IP of the last untrusted proxy in the chain,
not the original client IP.  You WILL NOT get the location
corresponding to the original client IP for any request sent
through a non-whitelisted proxy.

21
22
23
# File 'lib/geocoder/request.rb', line 21

def safe_location
  @safe_location ||= Geocoder.search(ip, ip_address: true).first
end