roda 3.42.0 → 3.43.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 +4 -4
- data/CHANGELOG +4 -0
- data/doc/release_notes/3.43.0.txt +34 -0
- data/lib/roda/plugins/host_authorization.rb +156 -0
- data/lib/roda/version.rb +1 -1
- metadata +6 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 6bca86a88a2d0e6fc0952ec2b2a91aa2e4d6dc0374546d5ac9d74d5b39a30abc
|
4
|
+
data.tar.gz: 20044ea95c1b1efabc06d8cda365384193c33ddd8140c7c91f9977483975e004
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 2281aac1898e6a81c114023fefb15f77c14ad1ebcd37daad10a1f5c51815c0d175c68a052627bb5025e02c92821d6c64cf6eb3757d801b277b771e73daddc20f
|
7
|
+
data.tar.gz: a55217f87afb2989312e62804715b65c8213997699b187f52a78f8a96dfb592a62ee7adeb029a52663631887ef5cdd15c746930cf9dfba437b744df159b7090c
|
data/CHANGELOG
CHANGED
@@ -0,0 +1,34 @@
|
|
1
|
+
= New Features
|
2
|
+
|
3
|
+
* A host_authorization plugin has been added to verify the requested
|
4
|
+
Host header is authorized. Using it can prevent DNS rebinding
|
5
|
+
attacks in cases where the application can receive requests for
|
6
|
+
arbitrary hosts.
|
7
|
+
|
8
|
+
To check for authorized hosts in your routing tree, you call the
|
9
|
+
check_host_authorization! method. For example, if you want to
|
10
|
+
check for authorized hosts after serving requests for public
|
11
|
+
files, you could do:
|
12
|
+
|
13
|
+
plugin :public
|
14
|
+
plugin :host_authorization, 'my-domain-name.example.com'
|
15
|
+
|
16
|
+
route do |r|
|
17
|
+
r.public
|
18
|
+
check_host_authorized!
|
19
|
+
|
20
|
+
# ... rest of routing tree
|
21
|
+
end
|
22
|
+
|
23
|
+
In addition to handling single domain names via a string, you can
|
24
|
+
provide an array of domain names, a regexp to match again, or a
|
25
|
+
proc.
|
26
|
+
|
27
|
+
By default, requests using unauthorized hosts receive an empty 403
|
28
|
+
response. If you would like to customize the response, you can
|
29
|
+
pass a block when loading the plugin:
|
30
|
+
|
31
|
+
plugin :host_authorization, 'my-domain-name.example.com' do |r|
|
32
|
+
response.status = 403
|
33
|
+
"Response Body Here"
|
34
|
+
end
|
@@ -0,0 +1,156 @@
|
|
1
|
+
# frozen-string-literal: true
|
2
|
+
|
3
|
+
#
|
4
|
+
class Roda
|
5
|
+
module RodaPlugins
|
6
|
+
# The host_authorization plugin allows configuring an authorized host or
|
7
|
+
# an array of authorized hosts. Then in the routing tree, you can check
|
8
|
+
# whether the request uses an authorized host via the +check_host_authorized!+
|
9
|
+
# method.
|
10
|
+
#
|
11
|
+
# If the request doesn't match one of the authorized hosts, the
|
12
|
+
# request processing stops at that point. Using this plugin can prevent
|
13
|
+
# DNS rebinding attacks if the application can receive requests for
|
14
|
+
# arbitrary hosts.
|
15
|
+
#
|
16
|
+
# By default, an empty response using status 403 will be returned for requests
|
17
|
+
# with unauthorized hosts.
|
18
|
+
#
|
19
|
+
# Because +check_host_authorized!+ is an instance method, you can easily choose
|
20
|
+
# to only check for authorization in certain routes, or to check it after
|
21
|
+
# other processing. For example, you could check for authorized hosts after
|
22
|
+
# serving static files, since the serving of static files should not be
|
23
|
+
# vulnerable to DNS rebinding attacks.
|
24
|
+
#
|
25
|
+
# = Usage
|
26
|
+
#
|
27
|
+
# In your routing tree, call the +check_host_authorized!+ method at the point you
|
28
|
+
# want to check for authorized hosts:
|
29
|
+
#
|
30
|
+
# plugin :host_authorization, 'www.example.com'
|
31
|
+
# plugin :public
|
32
|
+
#
|
33
|
+
# route do |r|
|
34
|
+
# r.public
|
35
|
+
# check_host_authorized!
|
36
|
+
#
|
37
|
+
# # ...
|
38
|
+
# end
|
39
|
+
#
|
40
|
+
# = Specifying authorized hosts
|
41
|
+
#
|
42
|
+
# For applications hosted on a single domain name, you can use a single string:
|
43
|
+
#
|
44
|
+
# plugin :host_authorization, 'www.example.com'
|
45
|
+
#
|
46
|
+
# For applications hosted on multiple domain names, you can use an array of strings:
|
47
|
+
#
|
48
|
+
# plugin :host_authorization, %w'www.example.com www.example2.com'
|
49
|
+
#
|
50
|
+
# For applications supporting arbitrary subdomains, you can use a regexp. If using
|
51
|
+
# a regexp, make sure you use <tt>\A<tt> and <tt>\z</tt> in your regexp, and restrict
|
52
|
+
# the allowed characters to the minimum required, otherwise you can potentionally
|
53
|
+
# introduce a security issue:
|
54
|
+
#
|
55
|
+
# plugin :host_authorization, /\A[-0-9a-f]+\.example\.com\z/
|
56
|
+
#
|
57
|
+
# For applications with more complex requirements, you can use a proc. Similarly
|
58
|
+
# to the regexp case, the proc should be aware the host contains user-submitted
|
59
|
+
# values, and not assume it is in any particular format:
|
60
|
+
#
|
61
|
+
# plugin :host_authorization, proc{|host| ExternalService.allowed_host?(host)}
|
62
|
+
#
|
63
|
+
# If an array of values is passed as the host argument, the host is authorized if
|
64
|
+
# it matches any value in the array. All host authorization checks use the
|
65
|
+
# <tt>===</tt> method, which is why it works for strings, regexps, and procs.
|
66
|
+
# It can also work with arbitrary objects that support <tt>===</tt>.
|
67
|
+
#
|
68
|
+
# For security reasons, only the +Host+ header is checked by default. If you are
|
69
|
+
# sure that your application is being run behind a forwarding proxy that sets the
|
70
|
+
# <tt>X-Forwarded-Host</tt> header, you should enable support for checking that
|
71
|
+
# header using the +:check_forwarded+ option:
|
72
|
+
#
|
73
|
+
# plugin :host_authorization, 'www.example.com', check_forwarded: true
|
74
|
+
#
|
75
|
+
# In this case, the trailing host in the <tt>X-Forwarded-Host</tt> header is checked,
|
76
|
+
# which should be the host set by the forwarding proxy closest to the application.
|
77
|
+
# In cases where multiple forwarding proxies are used that append to the
|
78
|
+
# <tt>X-Forwarded-Host</tt> header, you should not use this plugin.
|
79
|
+
#
|
80
|
+
# = Customizing behavior
|
81
|
+
#
|
82
|
+
# By default, an unauthorized host will receive an empty 403 response. You can
|
83
|
+
# customize this by passing a block when loading the plugin. For example, for
|
84
|
+
# sites using the render plugin, you could return a page that uses your default
|
85
|
+
# layout:
|
86
|
+
#
|
87
|
+
# plugin :render
|
88
|
+
# plugin :host_authorization, 'www.example.com' do |r|
|
89
|
+
# response.status = 403
|
90
|
+
# view(:content=>"<h1>Forbidden</h1>")
|
91
|
+
# end
|
92
|
+
#
|
93
|
+
# The block passed to this plugin is treated as a match block.
|
94
|
+
module HostAuthorization
|
95
|
+
def self.configure(app, host, opts=OPTS, &block)
|
96
|
+
app.opts[:host_authorization_host] = host
|
97
|
+
app.opts[:host_authorization_check_forwarded] = opts[:check_forwarded] if opts.key?(:check_forwarded)
|
98
|
+
|
99
|
+
if block
|
100
|
+
app.define_roda_method(:host_authorization_unauthorized, 1, &block)
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
module InstanceMethods
|
105
|
+
# Check whether the host is authorized. If not authorized, return a response
|
106
|
+
# immediately based on the plugin block.
|
107
|
+
def check_host_authorization!
|
108
|
+
r = @_request
|
109
|
+
return if host_authorized?(_convert_host_for_authorization(r.env["HTTP_HOST"].to_s.dup))
|
110
|
+
|
111
|
+
if opts[:host_authorization_check_forwarded] && (host = r.env["HTTP_X_FORWARDED_HOST"])
|
112
|
+
if i = host.rindex(',')
|
113
|
+
host = host[i+1, 10000000].to_s
|
114
|
+
end
|
115
|
+
host = _convert_host_for_authorization(host.strip)
|
116
|
+
|
117
|
+
if !host.empty? && host_authorized?(host)
|
118
|
+
return
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
r.on do
|
123
|
+
host_authorization_unauthorized(r)
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
private
|
128
|
+
|
129
|
+
# Remove the port information from the passed string (mutates the passed argument).
|
130
|
+
def _convert_host_for_authorization(host)
|
131
|
+
host.sub!(/:\d+\z/, "")
|
132
|
+
host
|
133
|
+
end
|
134
|
+
|
135
|
+
# Whether the host given is one of the authorized hosts for this application.
|
136
|
+
def host_authorized?(host, authorized_host = opts[:host_authorization_host])
|
137
|
+
case authorized_host
|
138
|
+
when Array
|
139
|
+
authorized_host.any?{|auth_host| host_authorized?(host, auth_host)}
|
140
|
+
else
|
141
|
+
authorized_host === host
|
142
|
+
end
|
143
|
+
end
|
144
|
+
|
145
|
+
# Action to take for unauthorized hosts. Sets a 403 status by default.
|
146
|
+
def host_authorization_unauthorized(_)
|
147
|
+
@_response.status = 403
|
148
|
+
nil
|
149
|
+
end
|
150
|
+
end
|
151
|
+
end
|
152
|
+
|
153
|
+
register_plugin(:host_authorization, HostAuthorization)
|
154
|
+
end
|
155
|
+
end
|
156
|
+
|
data/lib/roda/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: roda
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 3.
|
4
|
+
version: 3.43.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Jeremy Evans
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2021-
|
11
|
+
date: 2021-04-12 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rack
|
@@ -213,6 +213,7 @@ extra_rdoc_files:
|
|
213
213
|
- doc/release_notes/3.40.0.txt
|
214
214
|
- doc/release_notes/3.41.0.txt
|
215
215
|
- doc/release_notes/3.42.0.txt
|
216
|
+
- doc/release_notes/3.43.0.txt
|
216
217
|
- doc/release_notes/3.5.0.txt
|
217
218
|
- doc/release_notes/3.6.0.txt
|
218
219
|
- doc/release_notes/3.7.0.txt
|
@@ -262,6 +263,7 @@ files:
|
|
262
263
|
- doc/release_notes/3.40.0.txt
|
263
264
|
- doc/release_notes/3.41.0.txt
|
264
265
|
- doc/release_notes/3.42.0.txt
|
266
|
+
- doc/release_notes/3.43.0.txt
|
265
267
|
- doc/release_notes/3.5.0.txt
|
266
268
|
- doc/release_notes/3.6.0.txt
|
267
269
|
- doc/release_notes/3.7.0.txt
|
@@ -312,6 +314,7 @@ files:
|
|
312
314
|
- lib/roda/plugins/header_matchers.rb
|
313
315
|
- lib/roda/plugins/heartbeat.rb
|
314
316
|
- lib/roda/plugins/hooks.rb
|
317
|
+
- lib/roda/plugins/host_authorization.rb
|
315
318
|
- lib/roda/plugins/indifferent_params.rb
|
316
319
|
- lib/roda/plugins/json.rb
|
317
320
|
- lib/roda/plugins/json_parser.rb
|
@@ -401,7 +404,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
401
404
|
- !ruby/object:Gem::Version
|
402
405
|
version: '0'
|
403
406
|
requirements: []
|
404
|
-
rubygems_version: 3.2.
|
407
|
+
rubygems_version: 3.2.15
|
405
408
|
signing_key:
|
406
409
|
specification_version: 4
|
407
410
|
summary: Routing tree web toolkit
|