@@ -167,6 +167,44 @@ class HostAuthorizationTest < ActionDispatch::IntegrationTest
167167 assert_match "Blocked host: 127.0.0.1" , response . body
168168 end
169169
170+ test "blocks requests with spoofed relative X-FORWARDED-HOST" do
171+ @app = ActionDispatch ::HostAuthorization . new ( App , [ "www.example.com" ] )
172+
173+ get "/" , env : {
174+ "HTTP_X_FORWARDED_HOST" => "//randomhost.com" ,
175+ "HOST" => "www.example.com" ,
176+ "action_dispatch.show_detailed_exceptions" => true
177+ }
178+
179+ assert_response :forbidden
180+ assert_match "Blocked host: //randomhost.com" , response . body
181+ end
182+
183+ test "forwarded secondary hosts are allowed when permitted" do
184+ @app = ActionDispatch ::HostAuthorization . new ( App , ".domain.com" )
185+
186+ get "/" , env : {
187+ "HTTP_X_FORWARDED_HOST" => "example.com, my-sub.domain.com" ,
188+ "HOST" => "domain.com" ,
189+ }
190+
191+ assert_response :ok
192+ assert_equal "Success" , body
193+ end
194+
195+ test "forwarded secondary hosts are blocked when mismatch" do
196+ @app = ActionDispatch ::HostAuthorization . new ( App , "domain.com" )
197+
198+ get "/" , env : {
199+ "HTTP_X_FORWARDED_HOST" => "domain.com, evil.com" ,
200+ "HOST" => "domain.com" ,
201+ "action_dispatch.show_detailed_exceptions" => true
202+ }
203+
204+ assert_response :forbidden
205+ assert_match "Blocked host: evil.com" , response . body
206+ end
207+
170208 test "does not consider IP addresses in X-FORWARDED-HOST spoofed when disabled" do
171209 @app = ActionDispatch ::HostAuthorization . new ( App , nil )
172210
@@ -205,18 +243,67 @@ class HostAuthorizationTest < ActionDispatch::IntegrationTest
205243 assert_match "Blocked host: sub.domain.com" , response . body
206244 end
207245
246+ test "sub-sub domains should not be permitted" do
247+ @app = ActionDispatch ::HostAuthorization . new ( App , ".domain.com" )
248+
249+ get "/" , env : {
250+ "HOST" => "secondary.sub.domain.com" ,
251+ "action_dispatch.show_detailed_exceptions" => true
252+ }
253+
254+ assert_response :forbidden
255+ assert_match "Blocked host: secondary.sub.domain.com" , response . body
256+ end
257+
208258 test "forwarded hosts are allowed when permitted" do
209259 @app = ActionDispatch ::HostAuthorization . new ( App , ".domain.com" )
210260
211261 get "/" , env : {
212- "HTTP_X_FORWARDED_HOST" => "sub.domain.com" ,
262+ "HTTP_X_FORWARDED_HOST" => "my- sub.domain.com" ,
213263 "HOST" => "domain.com" ,
214264 }
215265
216266 assert_response :ok
217267 assert_equal "Success" , body
218268 end
219269
270+ test "lots of NG hosts" do
271+ ng_hosts = [
272+ "hacker%E3%80%82com" ,
273+ "hacker%00.com" ,
274+ "www.theirsite.com@yoursite.com" ,
275+ "hacker.com/test/" ,
276+ "hacker%252ecom" ,
277+ ".hacker.com" ,
278+ "/\/ \/ hacker.com/" ,
279+ "/hacker.com" ,
280+ "../hacker.com" ,
281+ ".hacker.com" ,
282+ "@hacker.com" ,
283+ "hacker.com" ,
284+ "hacker.com%23@example.com" ,
285+ "hacker.com/.jpg" ,
286+ "hacker.com\t example.com/" ,
287+ "hacker.com/example.com" ,
288+ "hacker.com\@ example.com" ,
289+ "hacker.com/example.com" ,
290+ "hacker.com/"
291+ ]
292+
293+ @app = ActionDispatch ::HostAuthorization . new ( App , "example.com" )
294+
295+ ng_hosts . each do |host |
296+ get "/" , env : {
297+ "HTTP_X_FORWARDED_HOST" => host ,
298+ "HOST" => "example.com" ,
299+ "action_dispatch.show_detailed_exceptions" => true
300+ }
301+
302+ assert_response :forbidden
303+ assert_match "Blocked host: #{ host } " , response . body
304+ end
305+ end
306+
220307 test "exclude matches allow any host" do
221308 @app = ActionDispatch ::HostAuthorization . new ( App , "only.com" , exclude : -> ( req ) { req . path == "/foo" } )
222309
0 commit comments