mirror of
https://github.com/spring-projects/spring-security.git
synced 2025-07-13 05:43:29 +00:00
Add support for allowedHostnames in StrictHttpFirewall
Introduce a new method `setAllowedHostnames` which perform the validation against untrusted hostnames. Fixes gh-4310
This commit is contained in:
parent
75e248344b
commit
e4e7363196
@ -228,6 +228,11 @@ class DummyRequest extends HttpServletRequestWrapper {
|
|||||||
public void setQueryString(String queryString) {
|
public void setQueryString(String queryString) {
|
||||||
this.queryString = queryString;
|
this.queryString = queryString;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getServerName() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
final class UnsupportedOperationExceptionInvocationHandler implements InvocationHandler {
|
final class UnsupportedOperationExceptionInvocationHandler implements InvocationHandler {
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2012-2017 the original author or authors.
|
* Copyright 2012-2020 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@ -16,14 +16,14 @@
|
|||||||
|
|
||||||
package org.springframework.security.web.firewall;
|
package org.springframework.security.web.firewall;
|
||||||
|
|
||||||
import javax.servlet.http.HttpServletRequest;
|
|
||||||
import javax.servlet.http.HttpServletResponse;
|
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
import javax.servlet.http.HttpServletResponse;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* <p>
|
* <p>
|
||||||
@ -59,10 +59,15 @@ import java.util.Set;
|
|||||||
* Rejects URLs that contain a URL encoded percent. See
|
* Rejects URLs that contain a URL encoded percent. See
|
||||||
* {@link #setAllowUrlEncodedPercent(boolean)}
|
* {@link #setAllowUrlEncodedPercent(boolean)}
|
||||||
* </li>
|
* </li>
|
||||||
|
* <li>
|
||||||
|
* Rejects hosts that are not allowed. See
|
||||||
|
* {@link #setAllowedHostnames(Collection)}
|
||||||
|
* </li>
|
||||||
* </ul>
|
* </ul>
|
||||||
*
|
*
|
||||||
* @see DefaultHttpFirewall
|
* @see DefaultHttpFirewall
|
||||||
* @author Rob Winch
|
* @author Rob Winch
|
||||||
|
* @author Eddú Meléndez
|
||||||
* @since 4.2.4
|
* @since 4.2.4
|
||||||
*/
|
*/
|
||||||
public class StrictHttpFirewall implements HttpFirewall {
|
public class StrictHttpFirewall implements HttpFirewall {
|
||||||
@ -82,6 +87,8 @@ public class StrictHttpFirewall implements HttpFirewall {
|
|||||||
|
|
||||||
private Set<String> decodedUrlBlacklist = new HashSet<String>();
|
private Set<String> decodedUrlBlacklist = new HashSet<String>();
|
||||||
|
|
||||||
|
private Collection<String> allowedHostnames;
|
||||||
|
|
||||||
public StrictHttpFirewall() {
|
public StrictHttpFirewall() {
|
||||||
urlBlacklistsAddAll(FORBIDDEN_SEMICOLON);
|
urlBlacklistsAddAll(FORBIDDEN_SEMICOLON);
|
||||||
urlBlacklistsAddAll(FORBIDDEN_FORWARDSLASH);
|
urlBlacklistsAddAll(FORBIDDEN_FORWARDSLASH);
|
||||||
@ -230,6 +237,13 @@ public class StrictHttpFirewall implements HttpFirewall {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setAllowedHostnames(Collection<String> allowedHostnames) {
|
||||||
|
if (allowedHostnames == null) {
|
||||||
|
throw new IllegalArgumentException("allowedHostnames cannot be null");
|
||||||
|
}
|
||||||
|
this.allowedHostnames = allowedHostnames;
|
||||||
|
}
|
||||||
|
|
||||||
private void urlBlacklistsAddAll(Collection<String> values) {
|
private void urlBlacklistsAddAll(Collection<String> values) {
|
||||||
this.encodedUrlBlacklist.addAll(values);
|
this.encodedUrlBlacklist.addAll(values);
|
||||||
this.decodedUrlBlacklist.addAll(values);
|
this.decodedUrlBlacklist.addAll(values);
|
||||||
@ -243,6 +257,7 @@ public class StrictHttpFirewall implements HttpFirewall {
|
|||||||
@Override
|
@Override
|
||||||
public FirewalledRequest getFirewalledRequest(HttpServletRequest request) throws RequestRejectedException {
|
public FirewalledRequest getFirewalledRequest(HttpServletRequest request) throws RequestRejectedException {
|
||||||
rejectedBlacklistedUrls(request);
|
rejectedBlacklistedUrls(request);
|
||||||
|
rejectedUntrustedHosts(request);
|
||||||
|
|
||||||
if (!isNormalized(request)) {
|
if (!isNormalized(request)) {
|
||||||
throw new RequestRejectedException("The request was rejected because the URL was not normalized.");
|
throw new RequestRejectedException("The request was rejected because the URL was not normalized.");
|
||||||
@ -272,6 +287,19 @@ public class StrictHttpFirewall implements HttpFirewall {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void rejectedUntrustedHosts(HttpServletRequest request) {
|
||||||
|
String serverName = request.getServerName();
|
||||||
|
if (serverName == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (this.allowedHostnames == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!this.allowedHostnames.contains(serverName)) {
|
||||||
|
throw new RequestRejectedException("The request was rejected because the domain " + serverName + " is untrusted.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public HttpServletResponse getFirewalledResponse(HttpServletResponse response) {
|
public HttpServletResponse getFirewalledResponse(HttpServletResponse response) {
|
||||||
return new FirewalledResponse(response);
|
return new FirewalledResponse(response);
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2012-2017 the original author or authors.
|
* Copyright 2012-2020 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@ -16,6 +16,8 @@
|
|||||||
|
|
||||||
package org.springframework.security.web.firewall;
|
package org.springframework.security.web.firewall;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.springframework.mock.web.MockHttpServletRequest;
|
import org.springframework.mock.web.MockHttpServletRequest;
|
||||||
|
|
||||||
@ -23,6 +25,7 @@ import static org.assertj.core.api.Assertions.fail;
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @author Rob Winch
|
* @author Rob Winch
|
||||||
|
* @author Eddú Meléndez
|
||||||
*/
|
*/
|
||||||
public class StrictHttpFirewallTests {
|
public class StrictHttpFirewallTests {
|
||||||
public String[] unnormalizedPaths = { "/..", "/./path/", "/path/path/.", "/path/path//.", "./path/../path//.",
|
public String[] unnormalizedPaths = { "/..", "/./path/", "/path/path/.", "/path/path//.", "./path/../path//.",
|
||||||
@ -373,4 +376,42 @@ public class StrictHttpFirewallTests {
|
|||||||
|
|
||||||
this.firewall.getFirewalledRequest(request);
|
this.firewall.getFirewalledRequest(request);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void getFirewalledRequestWhenTrustedDomainThenNoException() {
|
||||||
|
String host = "example.org";
|
||||||
|
this.request.addHeader("Host", host);
|
||||||
|
this.firewall.setAllowedHostnames(Arrays.asList(host));
|
||||||
|
|
||||||
|
try {
|
||||||
|
this.firewall.getFirewalledRequest(this.request);
|
||||||
|
} catch (RequestRejectedException fail) {
|
||||||
|
fail("Host " + host + " was rejected");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void getFirewalledRequestWhenUntrustedDomainThenException() {
|
||||||
|
String host = "example.org";
|
||||||
|
this.request.addHeader("Host", host);
|
||||||
|
this.firewall.setAllowedHostnames(Arrays.asList("myexample.org"));
|
||||||
|
|
||||||
|
try {
|
||||||
|
this.firewall.getFirewalledRequest(this.request);
|
||||||
|
fail("Host " + host + " was accepted");
|
||||||
|
} catch (RequestRejectedException expected) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void getFirewalledRequestWhenDefaultsThenNoException() {
|
||||||
|
String host = "example.org";
|
||||||
|
this.request.addHeader("Host", host);
|
||||||
|
|
||||||
|
try {
|
||||||
|
this.firewall.getFirewalledRequest(this.request);
|
||||||
|
} catch (RequestRejectedException fail) {
|
||||||
|
fail("Host " + host + " was rejected");
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user