module Geocoder::Request
def geocoder_reject_non_ipv4_addresses(ip_addresses)
def geocoder_reject_non_ipv4_addresses(ip_addresses) ips = [] for ip in ip_addresses begin valid_ip = IPAddr.new(ip) rescue valid_ip = false end ips << valid_ip.to_s if valid_ip end return ips.any? ? ips : ip_addresses end
def geocoder_reject_trusted_ip_addresses(ip_addresses)
default. (we don't want every lookup to return the location
been configured as trusted; includes private ranges by
use Rack's trusted_proxy?() method to filter out IPs that have
def geocoder_reject_trusted_ip_addresses(ip_addresses) ip_addresses.reject { |ip| trusted_proxy?(ip) } end
def geocoder_remove_port_from_addresses(ip_addresses)
def geocoder_remove_port_from_addresses(ip_addresses) ip_addresses.map do |ip| # IPv4 if ip.count('.') > 0 ip.split(':').first # IPv6 bracket notation elsif match = ip.match(/\[(\S+)\]/) match.captures.first # IPv6 bare notation else ip end end end
def geocoder_split_ip_addresses(ip_addresses)
def geocoder_split_ip_addresses(ip_addresses) ip_addresses ? ip_addresses.strip.split(/[,\s]+/) : [] end
def geocoder_spoofable_ip
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
def location
other security-sensitive application. Use safe_location
Don't use it in authorization/authentication code, or any
The location() method is vulnerable to trivial IP spoofing.
def location @location ||= Geocoder.search(geocoder_spoofable_ip, ip_address: true).first end
def safe_location
corresponding to the original client IP for any request sent
not the original client IP. You WILL NOT get the location
location for the IP of the last untrusted proxy in the chain,
whitelisted as trusted in your Rack config, you will get the
For requests that go through a proxy that you haven't
This safe_location() protects you from trivial IP spoofing.
def safe_location @safe_location ||= Geocoder.search(ip, ip_address: true).first end