PageRenderTime 25ms CodeModel.GetById 18ms RepoModel.GetById 0ms app.codeStats 0ms

/lib/core_ext/fix-overtrusting_in_non_standard_http_header.rb

https://bitbucket.org/gibwar/moebooru
Ruby | 134 lines | 95 code | 18 blank | 21 comment | 7 complexity | 8b9e63a4bfcfb8c46dae55db12c7e14a MD5 | raw file
Possible License(s): 0BSD
  1. # This monkeypatch is stolen from Rails 4.0 because the one in 3.2 is too
  2. # crappy that it sometimes returns invalid ip address (mainly from
  3. # X-Forwarded-For).
  4. # Reference: https://bitbucket.org/edogawaconan/moebooru/issue/113
  5. module ActionDispatch
  6. class RemoteIp
  7. class IpSpoofAttackError < StandardError ; end
  8. # IP addresses that are "trusted proxies" that can be stripped from
  9. # the comma-delimited list in the X-Forwarded-For header. See also:
  10. # http://en.wikipedia.org/wiki/Private_network#Private_IPv4_address_spaces
  11. # http://en.wikipedia.org/wiki/Private_network#Private_IPv6_addresses.
  12. remove_const :TRUSTED_PROXIES
  13. TRUSTED_PROXIES = %r{
  14. ^127\.0\.0\.1$ | # localhost
  15. ^::1$ |
  16. ^(10 | # private IP 10.x.x.x
  17. 172\.(1[6-9]|2[0-9]|3[0-1]) | # private IP in the range 172.16.0.0 .. 172.31.255.255
  18. 192\.168 | # private IP 192.168.x.x
  19. fc00:: # private IP fc00
  20. )\.
  21. }x
  22. attr_reader :check_ip, :proxies
  23. def initialize(app, check_ip_spoofing = true, custom_proxies = nil)
  24. @app = app
  25. @check_ip = check_ip_spoofing
  26. @proxies = case custom_proxies
  27. when Regexp
  28. custom_proxies
  29. when nil
  30. TRUSTED_PROXIES
  31. else
  32. Regexp.union(TRUSTED_PROXIES, custom_proxies)
  33. end
  34. end
  35. def call(env)
  36. env["action_dispatch.remote_ip"] = GetIp.new(env, self)
  37. @app.call(env)
  38. end
  39. class GetIp
  40. # IP v4 and v6 (with compression) validation regexp
  41. # https://gist.github.com/1289635
  42. VALID_IP = %r{
  43. (^(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[0-9]{1,2})(\.(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[0-9]{1,2})){3}$) | # ip v4
  44. (^(
  45. (([0-9A-Fa-f]{1,4}:){7}[0-9A-Fa-f]{1,4}) | # ip v6 not abbreviated
  46. (([0-9A-Fa-f]{1,4}:){6}:[0-9A-Fa-f]{1,4}) | # ip v6 with double colon in the end
  47. (([0-9A-Fa-f]{1,4}:){5}:([0-9A-Fa-f]{1,4}:)?[0-9A-Fa-f]{1,4}) | # - ip addresses v6
  48. (([0-9A-Fa-f]{1,4}:){4}:([0-9A-Fa-f]{1,4}:){0,2}[0-9A-Fa-f]{1,4}) | # - with
  49. (([0-9A-Fa-f]{1,4}:){3}:([0-9A-Fa-f]{1,4}:){0,3}[0-9A-Fa-f]{1,4}) | # - double colon
  50. (([0-9A-Fa-f]{1,4}:){2}:([0-9A-Fa-f]{1,4}:){0,4}[0-9A-Fa-f]{1,4}) | # - in the middle
  51. (([0-9A-Fa-f]{1,4}:){6} ((\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b)\.){3} (\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b)) | # ip v6 with compatible to v4
  52. (([0-9A-Fa-f]{1,4}:){1,5}:((\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b)\.){3}(\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b)) | # ip v6 with compatible to v4
  53. (([0-9A-Fa-f]{1,4}:){1}:([0-9A-Fa-f]{1,4}:){0,4}((\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b)\.){3}(\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b)) | # ip v6 with compatible to v4
  54. (([0-9A-Fa-f]{1,4}:){0,2}:([0-9A-Fa-f]{1,4}:){0,3}((\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b)\.){3}(\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b)) | # ip v6 with compatible to v4
  55. (([0-9A-Fa-f]{1,4}:){0,3}:([0-9A-Fa-f]{1,4}:){0,2}((\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b)\.){3}(\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b)) | # ip v6 with compatible to v4
  56. (([0-9A-Fa-f]{1,4}:){0,4}:([0-9A-Fa-f]{1,4}:){1}((\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b)\.){3}(\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b)) | # ip v6 with compatible to v4
  57. (::([0-9A-Fa-f]{1,4}:){0,5}((\b((25[0-5])|(1\d{2})|(2[0-4]\d) |(\d{1,2}))\b)\.){3}(\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b)) | # ip v6 with compatible to v4
  58. ([0-9A-Fa-f]{1,4}::([0-9A-Fa-f]{1,4}:){0,5}[0-9A-Fa-f]{1,4}) | # ip v6 with compatible to v4
  59. (::([0-9A-Fa-f]{1,4}:){0,6}[0-9A-Fa-f]{1,4}) | # ip v6 with double colon at the begining
  60. (([0-9A-Fa-f]{1,4}:){1,7}:) # ip v6 without ending
  61. )$)
  62. }x
  63. def initialize(env, middleware)
  64. @env = env
  65. @middleware = middleware
  66. @calculated_ip = false
  67. end
  68. # Determines originating IP address. REMOTE_ADDR is the standard
  69. # but will be wrong if the user is behind a proxy. Proxies will set
  70. # HTTP_CLIENT_IP and/or HTTP_X_FORWARDED_FOR, so we prioritize those.
  71. # HTTP_X_FORWARDED_FOR may be a comma-delimited list in the case of
  72. # multiple chained proxies. The first address which is in this list
  73. # if it's not a known proxy will be the originating IP.
  74. # Format of HTTP_X_FORWARDED_FOR:
  75. # client_ip, proxy_ip1, proxy_ip2...
  76. # http://en.wikipedia.org/wiki/X-Forwarded-For
  77. def calculate_ip
  78. client_ip = @env['HTTP_CLIENT_IP']
  79. forwarded_ip = ips_from('HTTP_X_FORWARDED_FOR').first
  80. remote_addrs = ips_from('REMOTE_ADDR')
  81. check_ip = client_ip && @middleware.check_ip
  82. if check_ip && forwarded_ip != client_ip
  83. # We don't know which came from the proxy, and which from the user
  84. raise IpSpoofAttackError, "IP spoofing attack?!" \
  85. "HTTP_CLIENT_IP=#{@env['HTTP_CLIENT_IP'].inspect}" \
  86. "HTTP_X_FORWARDED_FOR=#{@env['HTTP_X_FORWARDED_FOR'].inspect}"
  87. end
  88. client_ips = remove_proxies [client_ip, forwarded_ip, remote_addrs].flatten
  89. if client_ips.present?
  90. client_ips.first
  91. else
  92. # If there is no client ip we can return first valid proxy ip from REMOTE_ADDR
  93. remote_addrs.find { |ip| valid_ip? ip }
  94. end
  95. end
  96. def to_s
  97. return @ip if @calculated_ip
  98. @calculated_ip = true
  99. @ip = calculate_ip
  100. end
  101. private
  102. def ips_from(header)
  103. @env[header] ? @env[header].strip.split(/[,\s]+/) : []
  104. end
  105. def valid_ip?(ip)
  106. ip =~ VALID_IP
  107. end
  108. def not_a_proxy?(ip)
  109. ip !~ @middleware.proxies
  110. end
  111. def remove_proxies(ips)
  112. ips.select { |ip| valid_ip?(ip) && not_a_proxy?(ip) }
  113. end
  114. end
  115. end
  116. end