🖥

Rails - request.remote_ip / request.remote_addr の違い ( Proxy サーバー・ロードバラ

に公開

概要

Rails の request.remote_ip はリクエストに Proxyサーバー・ロードバランサーを挟む場合でも「良い感じ」で「元のIPアドレス」の推定をしてくれそう

このあたりのコードを読むと action_dispatch.remote_ip というものが呼び出されている

https://github.com/rails/rails/blob/e595d91ac2c07371b441f8b04781e7c03ac44135/actionpack/lib/action_dispatch/http/request.rb#L230

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

その先を読むと
https://github.com/rails/rails/blob/main/actionpack/lib/action_dispatch/middleware/remote_ip.rb#L109C1-L126C64

      # 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](https://web.archive.org/web/20170626095448/https://blog.gingerlime.com/2012/rails-ip-spoofing-vulnerabilities-and-protection/),
      # 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.

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

Trusted Proxy

Railsはデフォルトで「信頼できるプロクシ」の一覧を持っているようだ

    # The default trusted IPs list simply includes IP addresses that are guaranteed
    # by the IP specification to be private addresses. Those will not be the
    # ultimate client IP in production, and so are discarded. See
    # https://en.wikipedia.org/wiki/Private_network for details.
    TRUSTED_PROXIES = [
      "127.0.0.0/8",    # localhost IPv4 range, per RFC-3330
      "::1",            # localhost IPv6
      "fc00::/7",       # private IPv6 range fc00::/7
      "10.0.0.0/8",     # private IPv4 range 10.x.x.x
      "172.16.0.0/12",  # private IPv4 range 172.16.0.0 .. 172.31.255.255
      "192.168.0.0/16", # private IPv4 range 192.168.x.x
    ].map { |proxy| IPAddr.new(proxy) }

https://github.com/rails/rails/blob/main/actionpack/lib/action_dispatch/middleware/remote_ip.rb#L36C1-L39C65

なので、ロードバランサーが間にある構成でも、ロードバランサーが自己申告した HTTP_X_FORWARDED_FOR を信頼して request.remote_ip の値として返してくれていそうだ

Trusted Proxy の設定

なおRails config で trustex proxy を指定することもできるようだが、上記の通り、デフォルトでもプライベートのIPアドレスは心されている模様

config.action_dispatch.trusted_proxies

備考

素のIPアドレスを返すのは以下

  • request.remote_addr
  • request.env['REMOTE_ADDR']

HTTP_X_FORWARDED_FOR は以下で確認できる

  • request.env['HTTP_X_FORWARDED_FOR']

リンク

公開日時

2025-05-21

https://qiita.com/YumaInaura/items/f6c865d124ee4978ff42

Discussion