84

正如标题所示,您可以通过这两种方法获取客户端的 ip。我想知道是否有任何差异。谢谢你。

在源代码中有

“/usr/local/rvm/gems/ruby-1.9.3-p194/gems/actionpack-3.2.3/lib/action_dispatch/http/request.rb”257L,8741C

def ip
  @ip ||= super
end

# Originating IP address, usually set by the RemoteIp middleware.
def remote_ip
  @remote_ip ||= (@env["action_dispatch.remote_ip"] || ip).to_s
end

但我真的不知道其中的含义。

4

3 回答 3

90

request.ipip即使该客户端是代理,也会返回该客户端。

request.remote_ip更聪明并获得实际的客户ip。只有沿途的所有代理都设置了X-Forwarded-For标头,才能做到这一点。

于 2012-06-12T12:56:10.163 回答
70

请求.ip

request.ipRack::Request开箱即用提供的基本ip检测。它的当前定义可以在https://github.com/rack/rack/blob/master/lib/rack/request.rb找到。

它遵循的算法是首先检查REMOTE_ADDR标头中是否有任何不受信任的 IP 地址,如果找到,则选择列出的第一个。在这种情况下,“受信任的”IP 地址是来自保留的私有子网范围的 IP 地址,但请注意,它通过正则表达式匹配,这可能不是最好的方法。如果没有不受信任REMOTE_ADDR,那么它会查看HTTP_X_FORWARDED_FOR标题,并选择列出的最后一个不受信任的。如果这些都没有透露任何人,它会退回到REMOTE_ADDR可能是 127.0.0.1 的原始版本。

request.remote_ip

request.remote_ip是由ActionDispatch::Request(继承自Rack::Request)提供的增强型 IP 检测。这是问题中显示的代码。如您所见,它回退到request.ip除非action_dispatch.remote_ip@env. 这是由RemoteIp包含在默认 Rails 堆栈中的中间件完成的。你可以在https://github.com/rails/rails/blob/4-2-stable/actionpack/lib/action_dispatch/middleware/remote_ip.rb查看它的源代码。

如果启用,RemoteIp中间件会提供以下附加功能:

  • 提供可选但默认的 IP 欺骗检测。
  • 允许过滤配置代理地址,而不是仅依赖默认值。
  • 使用IPAddr该类来正确测试 IP 范围,而不是依赖于脆弱的正则表达式。
  • 用作HTTP_CLIENT_IP潜在 IP 的来源。

该算法类似于request.ip但略有不同。它使用HTTP_X_FORWARDED_FOR从最后一个到第一个,然后HTTP_CLIENT_IP从最后一个到第一个,最后是最后一个条目REMOTE_ADDR。它将所有这些都放在一个列表中并过滤代理,选择剩下的第一个。

IP 欺骗检测

提供的 IP 欺骗检测RemoteIp并不是特别强大,它所做的只是在最后一个HTTP_CLIENT_IP不在HTTP_X_FORWARDED_FOR. 这不一定是攻击的症状,但它可能是错误配置或使用不同约定的代理混合的症状,这些约定不会产生一致的结果。

使用哪个

在一个简单的设置中,您的代理都在本地或私有子网上,您可能可以侥幸逃脱request.ip,但request.remote_ip通常应该被认为是更好的选择。如果您使用具有公共互联网路由的代理(例如许多 CDN),则RemoteIp可以配置为开箱即用地为您提供正确的客户端 IP,而request.ip只有在您可以正确设置上游代理时才会REMOTE_ADDR正确。

安全配置

现在来解决蒂姆库尔特关于欺骗的评论。他绝对是对​​的,你应该担心,但他错了,如果你默认使用 nginx 或 haproxy,你可能会被欺骗。 RemoteIp旨在通过选择链中的最后一个IP 来防止欺骗。X-Forwarded-For规范指定每个代理将请求者的 IP 附加到链的末尾。通过过滤掉列入白名单的代理,可以保证最后一个条目是您的第一个列入白名单的代理写入的客户端 IP。当然有一个警告,那就是您实际上必须运行始终设置/附加的代理X-Forwarded-For,因此 Tim 的建议实际上应该相反:仅运行代理request.remote_ip时使用。

如何配置公共 IP 代理

这一切都很好,但ActionDispatch::RemoteIp已经在默认的中间件堆栈中。如何重新配置​​它以添加我的代理 CIDR?!

将此添加到您的application.rb

check_spoofing = true
proxies = ["23.235.32.0/20", "203.57.145.0/24"]
proxies += ActionDispatch::RemoteIp::TRUSTED_PROXIES
config.middleware.swap ActionDispatch::RemoteIp,
                       ActionDispatch::RemoteIp,
                       true,
                       proxies
于 2017-03-25T08:37:46.653 回答
45

从来源:

module ActionDispatch
  class Request < Rack::Request

    # ...

    def ip
      @ip ||= super
    end

    def remote_ip
      @remote_ip ||= (@env["action_dispatch.remote_ip"] || ip).to_s
    end

    # ...

  end
end

其中 Rack::Request 看起来像这样

module Rack
  class Request
     def ip
      remote_addrs = split_ip_addresses(@env['REMOTE_ADDR'])
      remote_addrs = reject_trusted_ip_addresses(remote_addrs)

      return remote_addrs.first if remote_addrs.any?

      forwarded_ips = split_ip_addresses(@env['HTTP_X_FORWARDED_FOR'])

      if client_ip = @env['HTTP_CLIENT_IP']
        # If forwarded_ips doesn't include the client_ip, it might be an
        # ip spoofing attempt, so we ignore HTTP_CLIENT_IP
        return client_ip if forwarded_ips.include?(client_ip)
      end

      return reject_trusted_ip_addresses(forwarded_ips).last || @env["REMOTE_ADDR"]
    end
  end
end 

所以remote_ip优先考虑action_dispatch.remote_ip。这是由ActionDispatch::RemoteIp中间件设置的。您可以在该中间件的源代码中看到它在被调用时正在检查欺骗攻击,因为它正在调用GetIp.new以设置该 env 变量。remote_ip正如 Clowerweb 解释的那样,这是必要的,因为即使通过本地代理也可以读取 IP 地址。

于 2012-06-12T13:06:10.513 回答