rack-protection 4.0.1 → 4.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: fac15a8950e2f553585d91567b99a9117078f5e3d7239b0650c645fa13bd260c
4
- data.tar.gz: 73484f5eb96e43a65dd7546ae5304ef84a978ead3a71ca6d20c72b2d2e0a8a4c
3
+ metadata.gz: 4c04192c3b3ef137443ac72495e4d948a544c1f7c7fd514f422045b870071b54
4
+ data.tar.gz: 5fc1d9022ef2d26e1c18dd56752883d3d99c6293fbd2a67708628bd414ab2f3f
5
5
  SHA512:
6
- metadata.gz: eac07f26ac2a3149b96aef3cfb22d9752d1eadaa3a7ffec44212b18965dbfaa568c6084b1ab2b1452fb753fc0fbcaf00b394f0db779bf6355495ec6c23c90d36
7
- data.tar.gz: dfb73e2654b88976e16478e6da98802b145db97391a3e3b090b7d6f45655331f4601ea1c2a0e23fadea4072c0a547d1575694a796a5be621de61cefdbe1fbb26
6
+ metadata.gz: 26d822dde064625a3520d8bd95b0b2499e4d94d081e8943bfeb4283cab18fd0f70d98b7e5e3cdea5298a75edc70a0a5fc44444e5563fab90db179136901758b6
7
+ data.tar.gz: d9618b6af6ffc69006e8404a1dc18dc4bd082a516042f25d804613e67438a27830f81b3fc87f2f82d2c7ff5e6607d461e30117515bf2f0be9ea36a0ff094f28d
data/README.md CHANGED
@@ -34,6 +34,10 @@ run MyApp
34
34
 
35
35
  # Prevented Attacks
36
36
 
37
+ ## DNS rebinding and other Host header attacks
38
+
39
+ * [`Rack::Protection::HostAuthorization`][host-authorization] (not included by `use Rack::Protection`)
40
+
37
41
  ## Cross Site Request Forgery
38
42
 
39
43
  Prevented by:
@@ -109,6 +113,7 @@ The instrumenter is passed a namespace (String) and environment (Hash). The name
109
113
  [escaped-params]: http://www.sinatrarb.com/protection/escaped_params
110
114
  [form-token]: http://www.sinatrarb.com/protection/form_token
111
115
  [frame-options]: http://www.sinatrarb.com/protection/frame_options
116
+ [host-authorization]: https://github.com/sinatra/sinatra/blob/main/rack-protection/lib/rack/protection/host_authorization.rb
112
117
  [http-origin]: http://www.sinatrarb.com/protection/http_origin
113
118
  [ip-spoofing]: http://www.sinatrarb.com/protection/ip_spoofing
114
119
  [json-csrf]: http://www.sinatrarb.com/protection/json_csrf
@@ -46,15 +46,15 @@ module Rack
46
46
  # Install the gem, then run the program:
47
47
  #
48
48
  # gem install 'rack-protection'
49
- # ruby server.rb
49
+ # puma server.ru
50
50
  #
51
- # Here is <tt>server.rb</tt>:
51
+ # Here is <tt>server.ru</tt>:
52
52
  #
53
53
  # require 'rack/protection'
54
54
  # require 'rack/session'
55
55
  #
56
56
  # app = Rack::Builder.app do
57
- # use Rack::Session::Cookie, secret: 'secret'
57
+ # use Rack::Session::Cookie, secret: 'CHANGEMEaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa'
58
58
  # use Rack::Protection::AuthenticityToken
59
59
  #
60
60
  # run -> (env) do
@@ -88,7 +88,7 @@ module Rack
88
88
  # end
89
89
  # end
90
90
  #
91
- # Rack::Handler::WEBrick.run app
91
+ # run app
92
92
  #
93
93
  # == Example: Customize which POST parameter holds the token
94
94
  #
@@ -58,6 +58,13 @@ module Rack
58
58
  result if (Array === result) && (result.size == 3)
59
59
  end
60
60
 
61
+ def debug(env, message)
62
+ return unless options[:logging]
63
+
64
+ l = options[:logger] || env['rack.logger'] || ::Logger.new(env['rack.errors'])
65
+ l.debug(message)
66
+ end
67
+
61
68
  def warn(env, message)
62
69
  return unless options[:logging]
63
70
 
@@ -0,0 +1,110 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rack/protection'
4
+ require 'ipaddr'
5
+
6
+ module Rack
7
+ module Protection
8
+ ##
9
+ # Prevented attack:: DNS rebinding and other Host header attacks
10
+ # Supported browsers:: all
11
+ # More infos:: https://en.wikipedia.org/wiki/DNS_rebinding
12
+ # https://portswigger.net/web-security/host-header
13
+ #
14
+ # Blocks HTTP requests with an unrecognized hostname in any of the following
15
+ # HTTP headers: Host, X-Forwarded-Host, Forwarded
16
+ #
17
+ # If you want to permit a specific hostname, you can pass in as the `:permitted_hosts` option:
18
+ #
19
+ # use Rack::Protection::HostAuthorization, permitted_hosts: ["www.example.org", "sinatrarb.com"]
20
+ #
21
+ # The `:allow_if` option can also be set to a proc to use custom allow/deny logic.
22
+ class HostAuthorization < Base
23
+ DOT = '.'
24
+ PORT_REGEXP = /:\d+\z/.freeze
25
+ SUBDOMAINS = /[a-z0-9\-.]+/.freeze
26
+ private_constant :DOT,
27
+ :PORT_REGEXP,
28
+ :SUBDOMAINS
29
+ default_reaction :deny
30
+ default_options allow_if: nil,
31
+ message: 'Host not permitted'
32
+
33
+ def initialize(*)
34
+ super
35
+ @permitted_hosts = []
36
+ @domain_hosts = []
37
+ @ip_hosts = []
38
+ @all_permitted_hosts = Array(options[:permitted_hosts])
39
+
40
+ @all_permitted_hosts.each do |host|
41
+ case host
42
+ when String
43
+ if host.start_with?(DOT)
44
+ domain = host[1..-1]
45
+ @permitted_hosts << domain.downcase
46
+ @domain_hosts << /\A#{SUBDOMAINS}#{Regexp.escape(domain)}\z/i
47
+ else
48
+ @permitted_hosts << host.downcase
49
+ end
50
+ when IPAddr then @ip_hosts << host
51
+ end
52
+ end
53
+ end
54
+
55
+ def accepts?(env)
56
+ return true if options[:allow_if]&.call(env)
57
+ return true if @all_permitted_hosts.empty?
58
+
59
+ request = Request.new(env)
60
+ origin_host = extract_host(request.host_authority)
61
+ forwarded_host = extract_host(request.forwarded_authority)
62
+
63
+ debug env, "#{self.class} " \
64
+ "@all_permitted_hosts=#{@all_permitted_hosts.inspect} " \
65
+ "@permitted_hosts=#{@permitted_hosts.inspect} " \
66
+ "@domain_hosts=#{@domain_hosts.inspect} " \
67
+ "@ip_hosts=#{@ip_hosts.inspect} " \
68
+ "origin_host=#{origin_host.inspect} " \
69
+ "forwarded_host=#{forwarded_host.inspect}"
70
+
71
+ if host_permitted?(origin_host)
72
+ if forwarded_host.nil?
73
+ true
74
+ else
75
+ host_permitted?(forwarded_host)
76
+ end
77
+ else
78
+ false
79
+ end
80
+ end
81
+
82
+ private
83
+
84
+ def extract_host(authority)
85
+ authority.to_s.split(PORT_REGEXP).first&.downcase
86
+ end
87
+
88
+ def host_permitted?(host)
89
+ exact_match?(host) || domain_match?(host) || ip_match?(host)
90
+ end
91
+
92
+ def exact_match?(host)
93
+ @permitted_hosts.include?(host)
94
+ end
95
+
96
+ def domain_match?(host)
97
+ return false if host.nil?
98
+ return false if host.start_with?(DOT)
99
+
100
+ @domain_hosts.any? { |domain_host| host.match?(domain_host) }
101
+ end
102
+
103
+ def ip_match?(host)
104
+ @ip_hosts.any? { |ip_host| ip_host.include?(host) }
105
+ rescue IPAddr::InvalidAddressError
106
+ false
107
+ end
108
+ end
109
+ end
110
+ end
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Rack
4
4
  module Protection
5
- VERSION = '4.0.1'
5
+ VERSION = '4.1.0'
6
6
  end
7
7
  end
@@ -12,6 +12,7 @@ module Rack
12
12
  autoload :EscapedParams, 'rack/protection/escaped_params'
13
13
  autoload :FormToken, 'rack/protection/form_token'
14
14
  autoload :FrameOptions, 'rack/protection/frame_options'
15
+ autoload :HostAuthorization, 'rack/protection/host_authorization'
15
16
  autoload :HttpOrigin, 'rack/protection/http_origin'
16
17
  autoload :IPSpoofing, 'rack/protection/ip_spoofing'
17
18
  autoload :JsonCsrf, 'rack/protection/json_csrf'
metadata CHANGED
@@ -1,13 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rack-protection
3
3
  version: !ruby/object:Gem::Version
4
- version: 4.0.1
4
+ version: 4.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - https://github.com/sinatra/sinatra/graphs/contributors
8
+ autorequire:
8
9
  bindir: bin
9
10
  cert_chain: []
10
- date: 1980-01-02 00:00:00.000000000 Z
11
+ date: 2024-11-18 00:00:00.000000000 Z
11
12
  dependencies:
12
13
  - !ruby/object:Gem::Dependency
13
14
  name: base64
@@ -77,6 +78,7 @@ files:
77
78
  - lib/rack/protection/escaped_params.rb
78
79
  - lib/rack/protection/form_token.rb
79
80
  - lib/rack/protection/frame_options.rb
81
+ - lib/rack/protection/host_authorization.rb
80
82
  - lib/rack/protection/http_origin.rb
81
83
  - lib/rack/protection/ip_spoofing.rb
82
84
  - lib/rack/protection/json_csrf.rb
@@ -98,6 +100,7 @@ metadata:
98
100
  homepage_uri: http://sinatrarb.com/protection/
99
101
  documentation_uri: https://www.rubydoc.info/gems/rack-protection
100
102
  rubygems_mfa_required: 'true'
103
+ post_install_message:
101
104
  rdoc_options: []
102
105
  require_paths:
103
106
  - lib
@@ -112,7 +115,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
112
115
  - !ruby/object:Gem::Version
113
116
  version: '0'
114
117
  requirements: []
115
- rubygems_version: 3.6.7
118
+ rubygems_version: 3.5.22
119
+ signing_key:
116
120
  specification_version: 4
117
121
  summary: Protect against typical web attacks, works with all Rack apps, including
118
122
  Rails.