Skip to content

Commit f712c55

Browse files
eddumelendezjzheaux
authored andcommitted
Add support for allowedHostnames in StrictHttpFirewall
Introduce a new method `setAllowedHostnames` which perform the validation against untrusted hostnames. Fixes gh-4310
1 parent a5cfd9f commit f712c55

File tree

3 files changed

+48
-3
lines changed

3 files changed

+48
-3
lines changed

web/src/main/java/org/springframework/security/web/FilterInvocation.java

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -228,10 +228,15 @@ public String getQueryString() {
228228
public void setQueryString(String queryString) {
229229
this.queryString = queryString;
230230
}
231+
232+
@Override
233+
public String getServerName() {
234+
return null;
235+
}
231236
}
232237

233238
final class UnsupportedOperationExceptionInvocationHandler implements InvocationHandler {
234239
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
235240
throw new UnsupportedOperationException(method + " is not supported");
236241
}
237-
}
242+
}

web/src/main/java/org/springframework/security/web/firewall/StrictHttpFirewall.java

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2012-2017 the original author or authors.
2+
* Copyright 2012-2019 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -26,6 +26,7 @@
2626
import java.util.HashSet;
2727
import java.util.List;
2828
import java.util.Set;
29+
import java.util.function.Predicate;
2930

3031
/**
3132
* <p>
@@ -66,10 +67,15 @@
6667
* Rejects URLs that contain a URL encoded percent. See
6768
* {@link #setAllowUrlEncodedPercent(boolean)}
6869
* </li>
70+
* <li>
71+
* Rejects hosts that are not allowed. See
72+
* {@link #setAllowedHostnames(Predicate)}
73+
* </li>
6974
* </ul>
7075
*
7176
* @see DefaultHttpFirewall
7277
* @author Rob Winch
78+
* @author Eddú Meléndez
7379
* @since 4.2.4
7480
*/
7581
public class StrictHttpFirewall implements HttpFirewall {
@@ -98,6 +104,8 @@ public class StrictHttpFirewall implements HttpFirewall {
98104

99105
private Set<String> allowedHttpMethods = createDefaultAllowedHttpMethods();
100106

107+
private Predicate<String> allowedHostnames = hostname -> true;
108+
101109
public StrictHttpFirewall() {
102110
urlBlacklistsAddAll(FORBIDDEN_SEMICOLON);
103111
urlBlacklistsAddAll(FORBIDDEN_FORWARDSLASH);
@@ -297,6 +305,13 @@ public void setAllowUrlEncodedPercent(boolean allowUrlEncodedPercent) {
297305
}
298306
}
299307

308+
public void setAllowedHostnames(Predicate<String> allowedHostnames) {
309+
if (allowedHostnames == null) {
310+
throw new IllegalArgumentException("allowedHostnames cannot be null");
311+
}
312+
this.allowedHostnames = allowedHostnames;
313+
}
314+
300315
private void urlBlacklistsAddAll(Collection<String> values) {
301316
this.encodedUrlBlacklist.addAll(values);
302317
this.decodedUrlBlacklist.addAll(values);
@@ -311,6 +326,7 @@ private void urlBlacklistsRemoveAll(Collection<String> values) {
311326
public FirewalledRequest getFirewalledRequest(HttpServletRequest request) throws RequestRejectedException {
312327
rejectForbiddenHttpMethod(request);
313328
rejectedBlacklistedUrls(request);
329+
rejectedUntrustedHosts(request);
314330

315331
if (!isNormalized(request)) {
316332
throw new RequestRejectedException("The request was rejected because the URL was not normalized.");
@@ -352,6 +368,13 @@ private void rejectedBlacklistedUrls(HttpServletRequest request) {
352368
}
353369
}
354370

371+
private void rejectedUntrustedHosts(HttpServletRequest request) {
372+
String serverName = request.getServerName();
373+
if (serverName != null && !this.allowedHostnames.test(serverName)) {
374+
throw new RequestRejectedException("The request was rejected because the domain " + serverName + " is untrusted.");
375+
}
376+
}
377+
355378
@Override
356379
public HttpServletResponse getFirewalledResponse(HttpServletResponse response) {
357380
return new FirewalledResponse(response);

web/src/test/java/org/springframework/security/web/firewall/StrictHttpFirewallTests.java

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2012-2017 the original author or authors.
2+
* Copyright 2012-2019 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -29,6 +29,7 @@
2929

3030
/**
3131
* @author Rob Winch
32+
* @author Eddú Meléndez
3233
*/
3334
public class StrictHttpFirewallTests {
3435
public String[] unnormalizedPaths = { "/..", "/./path/", "/path/path/.", "/path/path//.", "./path/../path//.",
@@ -524,4 +525,20 @@ public void getFirewalledRequestWhenRemoveFromDecodedUrlBlacklistThenNoException
524525
this.firewall.getDecodedUrlBlacklist().removeAll(Arrays.asList("//"));
525526
assertThatCode(() -> this.firewall.getFirewalledRequest(request)).doesNotThrowAnyException();
526527
}
528+
529+
@Test
530+
public void getFirewalledRequestWhenTrustedDomainThenNoException() {
531+
this.request.addHeader("Host", "example.org");
532+
this.firewall.setAllowedHostnames(hostname -> hostname.equals("example.org"));
533+
534+
assertThatCode(() -> this.firewall.getFirewalledRequest(this.request)).doesNotThrowAnyException();
535+
}
536+
537+
@Test(expected = RequestRejectedException.class)
538+
public void getFirewalledRequestWhenUntrustedDomainThenException() {
539+
this.request.addHeader("Host", "example.org");
540+
this.firewall.setAllowedHostnames(hostname -> hostname.equals("myexample.org"));
541+
542+
this.firewall.getFirewalledRequest(this.request);
543+
}
527544
}

0 commit comments

Comments
 (0)