класс ActionDispatch::RemoteIp::GetIp
Класс GetIp существует как способ отложить обработку данных запроса в фактический IP-адрес. Если вызывается метод ActionDispatch::Request#remote_ip, этот класс рассчитает значение и затем кеширует его.
Публичные Классовые Методы
# File actionpack/lib/action_dispatch/middleware/remote_ip.rb, line 88 def initialize(req, check_ip, proxies) @req = req @check_ip = check_ip @proxies = proxies end
Публичные Методы Инстансов
# File actionpack/lib/action_dispatch/middleware/remote_ip.rb, line 112
def calculate_ip
# Set by the Rack web server, this is a single value.
remote_addr = ips_from(@req.remote_addr).last
# Could be a CSV list and/or repeated headers that were concatenated.
client_ips = ips_from(@req.client_ip).reverse
forwarded_ips = ips_from(@req.x_forwarded_for).reverse
# +Client-Ip+ and +X-Forwarded-For+ should not, generally, both be set.
# If they are both set, it means that either:
#
# 1) This request passed through two proxies with incompatible IP header
# conventions.
# 2) The client passed one of +Client-Ip+ or +X-Forwarded-For+
# (whichever the proxy servers weren't using) themselves.
#
# Either way, there is no way for us to determine which header is the
# right one after the fact. Since we have no idea, if we are concerned
# about IP spoofing we need to give up and explode. (If you're not
# concerned about IP spoofing you can turn the +ip_spoofing_check+
# option off.)
should_check_ip = @check_ip && client_ips.last && forwarded_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=#{@req.client_ip.inspect} " \
"HTTP_X_FORWARDED_FOR=#{@req.x_forwarded_for.inspect}"
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 Проходит по различным заголовкам IP-адресов, ищет IP-адрес, который, скорее всего, является адресом фактического удалённого клиента, делающего этот запрос.
REMOTE_ADDR будет верным, если запрос отправлен непосредственно в Ruby-процесс, например, на Heroku. Когда запрос обрабатывается прокси-сервером, таким как HAProxy или NGINX, IP-адрес, который сделал исходный запрос, будет помещён в заголовок X-Forwarded-For. Если есть несколько прокси, этот заголовок может содержать список IP-адресов. Другие прокси-сервисы устанавливают заголовок Client-Ip, поэтому мы проверяем и его.
Как обсуждается в этой статье о Spoofing IP-адресов в Rails, в то время как первый IP-адрес в списке, скорее всего, является «исходным» IP-адресом, он также мог быть установлено клиентом злонамеренно.
Для того, чтобы найти первый адрес, который (вероятно) является точным, мы берём список IP-адресов, удаляем известные и доверенные прокси, и затем берём последний оставшийся адрес, который, предположительно, был установлен одним из этих прокси.
# File actionpack/lib/action_dispatch/middleware/remote_ip.rb, line 154 def to_s @ip ||= calculate_ip end
Кеширует значение, возвращаемое методом calculate_ip, и возвращает его для использования ActionDispatch::Request.
Приватные Методы Инстансов
# File actionpack/lib/action_dispatch/middleware/remote_ip.rb, line 176
def filter_proxies(ips) # :doc:
ips.reject do |ip|
@proxies.any? { |proxy| proxy === ip }
end
end # File actionpack/lib/action_dispatch/middleware/remote_ip.rb, line 160
def ips_from(header) # :doc:
return [] unless header
# Split the comma-separated list into an array of strings.
ips = header.strip.split(/[,\s]+/)
ips.select do |ip|
begin
# Only return IPs that are valid according to the IPAddr#new method.
range = IPAddr.new(ip).to_range
# We want to make sure nobody is sneaking a netmask in.
range.begin == range.end
rescue ArgumentError
nil
end
end
end
© 2004–2018 David Heinemeier Hansson
Licensed under the MIT License.