класс ActionDispatch::RemoteIp::GetIp
Класс GetIp предназначен для отложенной обработки данных запроса с целью получения фактического IP-адреса. Если вызывается метод ActionDispatch::Request#remote_ip, этот класс вычисляет значение и затем сохраняет его для повторного использования.
Публичные методы класса
# File actionpack/lib/action_dispatch/middleware/remote_ip.rb, line 105 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 129
def calculate_ip
# Set by the Rack web server, this is a single value.
remote_addr = sanitize_ips(ips_from(@req.remote_addr)).last
# Could be a CSV list and/or repeated headers that were concatenated.
client_ips = sanitize_ips(ips_from(@req.client_ip)).reverse!
forwarded_ips = sanitize_ips(@req.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}" \
" HTTP_FORWARDED=" + @req.forwarded_for.map { "for=#{_1}" }.join(", ").inspect if @req.forwarded_for.any?
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
ips.compact!
# If every single IP option is in the trusted list, return the IP that's
# furthest away
filter_proxies(ips + [remote_addr]).first || ips.last || remote_addr
end Просматривает заголовки с различными IP-адресами, чтобы определить адрес, который с наибольшей вероятностью является адресом удалённого клиента, отправившего этот запрос.
Значение REMOTE_ADDR будет верным, если запрос напрямую направлен процессу Ruby, например, на Heroku. Если запрос проходит через другой сервер, например HAProxy или NGINX, IP-адрес, с которого был отправлен исходный запрос, будет помещён в заголовок X-Forwarded-For. Если прокси-серверов несколько, этот заголовок может содержать список IP-адресов. Другие прокси-сервисы вместо него задают заголовок Client-Ip, поэтому мы проверяем и его.
Как обсуждается в этой статье о подмене IP-адресов в Rails, хотя первый IP-адрес в списке, вероятно, является IP-адресом «источника», клиент также мог установить его злонамеренно.
Чтобы найти первый (вероятно) достоверный адрес, мы берём список IP-адресов, удаляем известные и доверенные прокси-серверы, а затем выбираем последний оставшийся адрес, который, предположительно, был задан одним из этих прокси-серверов.
# File actionpack/lib/action_dispatch/middleware/remote_ip.rb, line 174 def to_s @ip ||= calculate_ip end
Сохраняет значение, возвращённое методом calculate_ip, для повторного использования и возвращает его методу ActionDispatch::Request.
Приватные методы экземпляра
# File actionpack/lib/action_dispatch/middleware/remote_ip.rb, line 197
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 179 def ips_from(header) # :doc: return [] unless header # Split the comma-separated list into an array of strings. header.strip.split(/[,\s]+/) end
# File actionpack/lib/action_dispatch/middleware/remote_ip.rb, line 185
def sanitize_ips(ips) # :doc:
ips.select! do |ip|
# 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
ips
end
© 2004–2021 David Heinemeier Hansson
Licensed under the MIT License.