Ruby on Rails | Screencasts | Download | Documentation | Weblog | Community | Source

Changeset 9124

Show
Ignore:
Timestamp:
03/28/08 21:38:01 (2 months ago)
Author:
bitsweat
Message:

Avoid remote_ip spoofing

Files:

Legend:

Unmodified
Added
Removed
Modified
Copied
Moved
  • trunk/actionpack/CHANGELOG

    r9115 r9124  
    11*SVN* 
     2 
     3* Avoid remote_ip spoofing.  [Brian Candler] 
    24 
    35* Added support for regexp flags like ignoring case in the :requirements part of routes declarations #11421 [NeilW] 
  • trunk/actionpack/lib/action_controller/request.rb

    r9113 r9124  
    125125    alias xhr? :xml_http_request? 
    126126 
     127    # Which IP addresses are "trusted proxies" that can be stripped from 
     128    # the right-hand-side of X-Forwarded-For 
     129    TRUSTED_PROXIES = /^127\.0\.0\.1$|^(10|172\.(1[6-9]|2[0-9]|30|31)|192\.168)\./i 
     130 
    127131    # Determine originating IP address.  REMOTE_ADDR is the standard 
    128132    # but will fail if the user is behind a proxy.  HTTP_CLIENT_IP and/or 
    129     # HTTP_X_FORWARDED_FOR are set by proxies so check for these before 
    130     # falling back to REMOTE_ADDR.  HTTP_X_FORWARDED_FOR may be a comma- 
    131     # delimited list in the case of multiple chained proxies; the first is 
    132     # the originating IP. 
    133     # 
    134     # Security note: do not use if IP spoofing is a concern for your 
    135     # application. Since remote_ip checks HTTP headers for addresses forwarded 
    136     # by proxies, the client may send any IP. remote_addr can't be spoofed but 
    137     # also doesn't work behind a proxy, since it's always the proxy's IP. 
     133    # HTTP_X_FORWARDED_FOR are set by proxies so check for these if 
     134    # REMOTE_ADDR is a proxy.  HTTP_X_FORWARDED_FOR may be a comma- 
     135    # delimited list in the case of multiple chained proxies; the last 
     136    # address which is not trusted is the originating IP. 
     137 
    138138    def remote_ip 
    139       return @env['HTTP_CLIENT_IP'] if @env.include? 'HTTP_CLIENT_IP' 
     139      if TRUSTED_PROXIES !~ @env['REMOTE_ADDR'] 
     140        return @env['REMOTE_ADDR'] 
     141      end 
     142 
     143      if @env.include? 'HTTP_CLIENT_IP' 
     144        if @env.include? 'HTTP_X_FORWARDED_FOR' 
     145          # We don't know which came from the proxy, and which from the user 
     146          raise ActionControllerError.new(<<EOM) 
     147IP spoofing attack?! 
     148HTTP_CLIENT_IP=#{@env['HTTP_CLIENT_IP'].inspect} 
     149HTTP_X_FORWARDED_FOR=#{@env['HTTP_X_FORWARDED_FOR'].inspect} 
     150EOM 
     151        end 
     152        return @env['HTTP_CLIENT_IP'] 
     153      end 
    140154 
    141155      if @env.include? 'HTTP_X_FORWARDED_FOR' then 
    142         remote_ips = @env['HTTP_X_FORWARDED_FOR'].split(',').reject do |ip| 
    143           ip.strip =~ /^unknown$|^(10|172\.(1[6-9]|2[0-9]|30|31)|192\.168)\./i 
    144         end 
    145  
    146         return remote_ips.first.strip unless remote_ips.empty? 
     156        remote_ips = @env['HTTP_X_FORWARDED_FOR'].split(',') 
     157        while remote_ips.size > 1 && TRUSTED_PROXIES =~ remote_ips.last.strip 
     158          remote_ips.pop 
     159        end 
     160 
     161        return remote_ips.last.strip 
    147162      end 
    148163 
  • trunk/actionpack/test/controller/request_test.rb

    r9113 r9124  
    1414 
    1515    @request.env['HTTP_CLIENT_IP'] = '2.3.4.5' 
     16    assert_equal '1.2.3.4', @request.remote_ip 
     17 
     18    @request.remote_addr = '192.168.0.1' 
    1619    assert_equal '2.3.4.5', @request.remote_ip 
    1720    @request.env.delete 'HTTP_CLIENT_IP' 
    1821 
     22    @request.remote_addr = '1.2.3.4' 
     23    @request.env['HTTP_X_FORWARDED_FOR'] = '3.4.5.6' 
     24    assert_equal '1.2.3.4', @request.remote_ip 
     25 
     26    @request.remote_addr = '127.0.0.1' 
    1927    @request.env['HTTP_X_FORWARDED_FOR'] = '3.4.5.6' 
    2028    assert_equal '3.4.5.6', @request.remote_ip 
     
    3644 
    3745    @request.env['HTTP_X_FORWARDED_FOR'] = '127.0.0.1,3.4.5.6' 
    38     assert_equal '127.0.0.1', @request.remote_ip 
     46    assert_equal '3.4.5.6', @request.remote_ip 
    3947 
    4048    @request.env['HTTP_X_FORWARDED_FOR'] = 'unknown,192.168.0.1' 
    41     assert_equal '1.2.3.4', @request.remote_ip 
     49    assert_equal 'unknown', @request.remote_ip 
     50 
     51    @request.env['HTTP_X_FORWARDED_FOR'] = '9.9.9.9, 3.4.5.6, 10.0.0.1, 172.31.4.4' 
     52    assert_equal '3.4.5.6', @request.remote_ip 
     53 
     54    @request.env['HTTP_CLIENT_IP'] = '8.8.8.8' 
     55    e = assert_raises(ActionController::ActionControllerError) { 
     56      @request.remote_ip 
     57    } 
     58    assert_match /IP spoofing attack/, e.message 
     59    assert_match /HTTP_X_FORWARDED_FOR="9.9.9.9, 3.4.5.6, 10.0.0.1, 172.31.4.4"/, e.message 
     60    assert_match /HTTP_CLIENT_IP="8.8.8.8"/, e.message 
     61 
     62    @request.env.delete 'HTTP_CLIENT_IP' 
    4263    @request.env.delete 'HTTP_X_FORWARDED_FOR' 
    4364  end