ActionDispatch::RemoteIp::GetIp (original) (raw)

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.

Methods

C

F

I

N

T

Class Public methods

Source: show | on GitHub

def initialize(req, check_ip, proxies) @req = req @check_ip = check_ip @proxies = proxies end

Instance Public methods

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, 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.

Source: show | on GitHub

def calculate_ip

remote_addr = ips_from(@req.remote_addr).last

client_ips = ips_from(@req.client_ip).reverse! forwarded_ips = ips_from(@req.x_forwarded_for).reverse!

should_check_ip = @check_ip && client_ips.last && forwarded_ips.last if should_check_ip && !forwarded_ips.include?(client_ips.last)

raise IpSpoofAttackError, "IP spoofing attack?! " \
  "HTTP_CLIENT_IP=#{@req.client_ip.inspect} " \
  "HTTP_X_FORWARDED_FOR=#{@req.x_forwarded_for.inspect}"

end

ips = forwarded_ips + client_ips ips.compact!

filter_proxies(ips + [remote_addr]).first || ips.last || remote_addr end

Instance Private methods

Source: show | on GitHub

def filter_proxies(ips) ips.reject do |ip| @proxies.any? { |proxy| proxy === ip } end end

Source: show | on GitHub

def ips_from(header) return [] unless header

ips = header.strip.split(/[,\s]+/) ips.select! do |ip|

range = IPAddr.new(ip).to_range

range.begin == range.end

rescue ArgumentError nil end ips end