1
0
mirror of synced 2026-08-05 01:36:56 +00:00

Compare commits

...

15 Commits

Author SHA1 Message Date
Eleftheria Stein 37568780a1 Release 5.1.11.RELEASE 2020-06-03 17:48:40 -04:00
Josh Cummings d80b1865a5 Polish setAllowedHostnames
Added JavaDoc to method, including @since attribute

Issue gh-4310
2020-06-03 08:54:06 -06:00
Eddú Meléndez 52c80c78e5 Add support for allowedHostnames in StrictHttpFirewall
Introduce a new method `setAllowedHostnames` which perform the validation
against untrusted hostnames.

Fixes gh-4310
2020-06-03 08:53:59 -06:00
Eleftheria Stein ded83cc1b3 Update to jaxb-impl 2.3.3
Fixes gh-8634
2020-06-02 18:45:14 -04:00
Eleftheria Stein 7a1833c1df Update to okhttp 3.12.12
Fixes gh-8635
2020-06-02 18:45:14 -04:00
Eleftheria Stein e6630ea0f1 Update to mockwebserver 3.12.12
Fixes gh-8633
2020-06-02 18:45:14 -04:00
Eleftheria Stein 2400e8fde2 Update to Spring Boot 2.1.14.RELEASE
Fixes gh-8632
2020-06-02 18:45:14 -04:00
justmehyp f05d70a4a5 Remove unused field 'digester' in Md4PasswordEncoder
`private Digester digester;`  defined in Md4PasswordEncoder is never used. So remove it.

Closes gh-8553
2020-05-21 11:21:40 -05:00
Maksim Vinogradov 8bb4e72aff Prevent StackOverflowError for AccessControlEntryImpl.hashCode
Getting StackOverflowError when invoke AclImpl.hashCode because of
cross-references between AclImpl and AccessControlEntryImpl

Remove from AccessControlEntryImpl.hashCode method invocation of
acl.hashCode

fixes gh-5401
2020-05-21 10:08:56 -05:00
Rob Winch f58a262eb4 Revert "Create the CSRF token on the bounded elactic scheduler"
Reactor did not add bounded elastic until a later version.

This reverts commit c0154f2315.
2020-05-18 11:10:41 -05:00
cbornet c0154f2315 Create the CSRF token on the bounded elactic scheduler
The CSRF token is created with a call to UUID.randomUUID which is blocking.
This change ensures this blocking call is done on the bounded elastic scheduler which supports blocking calls.

Fixes gh-8128
2020-05-18 11:07:08 -05:00
Artyom Tarynin cea2b556d6 Update AntPathRequestMatcher.java
Fixes gh-8512
2020-05-14 10:36:30 -04:00
Dávid Kovács faa02e8bc0 Document NoOpPasswordEncoder will not be removed
This commit adds extension to deprecation notice.

Fixes gh-8506
2020-05-13 12:56:22 -05:00
Rob Winch d9f57492d4 Fix non-standard HTTP method for CsrfWebFilter
Closes gh-8452
2020-05-12 13:21:22 -05:00
Eleftheria Stein b007fdc333 Next development version 2020-05-06 16:27:44 -04:00
12 changed files with 118 additions and 31 deletions
@@ -1,5 +1,5 @@
/*
* Copyright 2004, 2005, 2006 Acegi Technology Pty Limited
* Copyright 2002-2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -131,10 +131,9 @@ public class AccessControlEntryImpl implements AccessControlEntry,
@Override
public int hashCode() {
int result = this.acl.hashCode();
result = 31 * result + this.permission.hashCode();
int result = this.permission.hashCode();
result = 31 * result + (this.id != null ? this.id.hashCode() : 0);
result = 31 * result + this.sid.hashCode();
result = 31 * result + (this.sid.hashCode());
result = 31 * result + (this.auditFailure ? 1 : 0);
result = 31 * result + (this.auditSuccess ? 1 : 0);
result = 31 * result + (this.granting ? 1 : 0);
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2016 the original author or authors.
* Copyright 2002-2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -560,6 +560,25 @@ public class AclImplTests {
childAcl.setParent(changeParentAcl);
}
@Test
public void hashCodeWithoutStackOverFlow() throws Exception {
//given
Sid sid = new PrincipalSid("pSid");
ObjectIdentity oid = new ObjectIdentityImpl("type", 1);
AclAuthorizationStrategy authStrategy = new AclAuthorizationStrategyImpl(new SimpleGrantedAuthority("role"));
PermissionGrantingStrategy grantingStrategy = new DefaultPermissionGrantingStrategy(new ConsoleAuditLogger());
AclImpl acl = new AclImpl(oid, 1L, authStrategy, grantingStrategy, null, null, false, sid);
AccessControlEntryImpl ace = new AccessControlEntryImpl(1L, acl, sid, BasePermission.READ, true, true, true);
Field fieldAces = FieldUtils.getField(AclImpl.class, "aces");
fieldAces.setAccessible(true);
List<AccessControlEntryImpl> aces = (List<AccessControlEntryImpl>) fieldAces.get(acl);
aces.add(ace);
//when - then none StackOverFlowError been raised
ace.hashCode();
}
// ~ Inner Classes
// ==================================================================================================
@@ -83,8 +83,6 @@ public class Md4PasswordEncoder implements PasswordEncoder {
private StringKeyGenerator saltGenerator = new Base64StringKeyGenerator();
private boolean encodeHashAsBase64;
private Digester digester;
public void setEncodeHashAsBase64(boolean encodeHashAsBase64) {
this.encodeHashAsBase64 = encodeHashAsBase64;
@@ -26,7 +26,8 @@ package org.springframework.security.crypto.password;
* @deprecated This PasswordEncoder is not secure. Instead use an
* adaptive one way function like BCryptPasswordEncoder, Pbkdf2PasswordEncoder, or
* SCryptPasswordEncoder. Even better use {@link DelegatingPasswordEncoder} which supports
* password upgrades.
* password upgrades. There are no plans to remove this support. It is deprecated to indicate that
* this is a legacy implementation and using it is considered insecure.
*/
@Deprecated
public final class NoOpPasswordEncoder implements PasswordEncoder {
+2 -2
View File
@@ -1,3 +1,3 @@
gaeVersion=1.9.80
springBootVersion=2.1.13.RELEASE
version=5.1.10.RELEASE
springBootVersion=2.1.14.RELEASE
version=5.1.11.RELEASE
+3 -3
View File
@@ -18,7 +18,7 @@ dependencyManagement {
}
dependencies {
dependency 'cglib:cglib-nodep:3.2.12'
dependency 'com.squareup.okhttp3:mockwebserver:3.12.10'
dependency 'com.squareup.okhttp3:mockwebserver:3.12.12'
dependency 'opensymphony:sitemesh:2.4.2'
dependency 'org.gebish:geb-spock:0.10.0'
dependency 'org.jasig.cas:cas-server-webapp:4.2.7'
@@ -56,10 +56,10 @@ dependencyManagement {
dependency 'com.nimbusds:lang-tag:1.4.3'
dependency 'com.nimbusds:nimbus-jose-jwt:6.0.2'
dependency 'com.nimbusds:oauth2-oidc-sdk:6.0'
dependency 'com.squareup.okhttp3:okhttp:3.12.10'
dependency 'com.squareup.okhttp3:okhttp:3.12.12'
dependency 'com.squareup.okio:okio:1.13.0'
dependency 'com.sun.xml.bind:jaxb-core:2.3.0.1'
dependency 'com.sun.xml.bind:jaxb-impl:2.3.2'
dependency 'com.sun.xml.bind:jaxb-impl:2.3.3'
dependency 'com.unboundid:unboundid-ldapsdk:4.0.14'
dependency 'com.vaadin.external.google:android-json:0.0.20131108.vaadin1'
dependency 'commons-cli:commons-cli:1.4'
@@ -228,10 +228,15 @@ class DummyRequest extends HttpServletRequestWrapper {
public void setQueryString(String queryString) {
this.queryString = queryString;
}
@Override
public String getServerName() {
return null;
}
}
final class UnsupportedOperationExceptionInvocationHandler implements InvocationHandler {
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
throw new UnsupportedOperationException(method + " is not supported");
}
}
}
@@ -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");
* you may not use this file except in compliance with the License.
@@ -16,16 +16,17 @@
package org.springframework.security.web.firewall;
import org.springframework.http.HttpMethod;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.function.Predicate;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.http.HttpMethod;
/**
* <p>
@@ -66,10 +67,15 @@ import java.util.Set;
* Rejects URLs that contain a URL encoded percent. See
* {@link #setAllowUrlEncodedPercent(boolean)}
* </li>
* <li>
* Rejects hosts that are not allowed. See
* {@link #setAllowedHostnames(Predicate)}
* </li>
* </ul>
*
* @see DefaultHttpFirewall
* @author Rob Winch
* @author Eddú Meléndez
* @since 4.2.4
*/
public class StrictHttpFirewall implements HttpFirewall {
@@ -96,6 +102,8 @@ public class StrictHttpFirewall implements HttpFirewall {
private Set<String> allowedHttpMethods = createDefaultAllowedHttpMethods();
private Predicate<String> allowedHostnames = hostname -> true;
public StrictHttpFirewall() {
urlBlacklistsAddAll(FORBIDDEN_SEMICOLON);
urlBlacklistsAddAll(FORBIDDEN_FORWARDSLASH);
@@ -277,6 +285,21 @@ public class StrictHttpFirewall implements HttpFirewall {
}
}
/**
* <p>
* Determines which hostnames should be allowed. The default is to allow any hostname.
* </p>
*
* @param allowedHostnames the predicate for testing hostnames
* @since 5.1.11
*/
public void setAllowedHostnames(Predicate<String> allowedHostnames) {
if (allowedHostnames == null) {
throw new IllegalArgumentException("allowedHostnames cannot be null");
}
this.allowedHostnames = allowedHostnames;
}
private void urlBlacklistsAddAll(Collection<String> values) {
this.encodedUrlBlacklist.addAll(values);
this.decodedUrlBlacklist.addAll(values);
@@ -291,6 +314,7 @@ public class StrictHttpFirewall implements HttpFirewall {
public FirewalledRequest getFirewalledRequest(HttpServletRequest request) throws RequestRejectedException {
rejectForbiddenHttpMethod(request);
rejectedBlacklistedUrls(request);
rejectedUntrustedHosts(request);
if (!isNormalized(request)) {
throw new RequestRejectedException("The request was rejected because the URL was not normalized.");
@@ -332,6 +356,13 @@ public class StrictHttpFirewall implements HttpFirewall {
}
}
private void rejectedUntrustedHosts(HttpServletRequest request) {
String serverName = request.getServerName();
if (serverName != null && !this.allowedHostnames.test(serverName)) {
throw new RequestRejectedException("The request was rejected because the domain " + serverName + " is untrusted.");
}
}
@Override
public HttpServletResponse getFirewalledResponse(HttpServletResponse response) {
return new FirewalledResponse(response);
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2017 the original author or authors.
* Copyright 2002-2020 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -55,6 +55,7 @@ import org.springframework.web.server.WebFilterChain;
* </p>
*
* @author Rob Winch
* @author Parikshit Dutta
* @since 5.0
*/
public class CsrfWebFilter implements WebFilter {
@@ -136,7 +137,7 @@ public class CsrfWebFilter implements WebFilter {
@Override
public Mono<MatchResult> matches(ServerWebExchange exchange) {
return Mono.just(exchange.getRequest())
.map(r -> r.getMethod())
.flatMap(r -> Mono.justOrEmpty(r.getMethod()))
.filter(m -> ALLOWED_METHODS.contains(m))
.flatMap(m -> MatchResult.notMatch())
.switchIfEmpty(MatchResult.match());
@@ -67,7 +67,7 @@ public final class AntPathRequestMatcher
/**
* Creates a matcher with the specific pattern which will match all HTTP methods in a
* case insensitive manner.
* case sensitive manner.
*
* @param pattern the ant pattern to use for matching
*/
@@ -76,7 +76,7 @@ public final class AntPathRequestMatcher
}
/**
* Creates a matcher with the supplied pattern and HTTP method in a case insensitive
* Creates a matcher with the supplied pattern and HTTP method in a case sensitive
* manner.
*
* @param pattern the ant pattern to use for matching
@@ -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");
* you may not use this file except in compliance with the License.
@@ -29,6 +29,7 @@ import org.springframework.mock.web.MockHttpServletRequest;
/**
* @author Rob Winch
* @author Eddú Meléndez
*/
public class StrictHttpFirewallTests {
public String[] unnormalizedPaths = { "/..", "/./path/", "/path/path/.", "/path/path//.", "./path/../path//.",
@@ -428,4 +429,20 @@ public class StrictHttpFirewallTests {
this.firewall.getFirewalledRequest(request);
}
@Test
public void getFirewalledRequestWhenTrustedDomainThenNoException() {
this.request.addHeader("Host", "example.org");
this.firewall.setAllowedHostnames(hostname -> hostname.equals("example.org"));
assertThatCode(() -> this.firewall.getFirewalledRequest(this.request)).doesNotThrowAnyException();
}
@Test(expected = RequestRejectedException.class)
public void getFirewalledRequestWhenUntrustedDomainThenException() {
this.request.addHeader("Host", "example.org");
this.firewall.setAllowedHostnames(hostname -> hostname.equals("myexample.org"));
this.firewall.getFirewalledRequest(this.request);
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2017 the original author or authors.
* Copyright 2002-2020 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -20,10 +20,14 @@ import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnitRunner;
import org.springframework.http.HttpMethod;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.mock.http.server.reactive.MockServerHttpRequest;
import org.springframework.mock.web.server.MockServerWebExchange;
import org.springframework.security.web.server.util.matcher.ServerWebExchangeMatcher;
import org.springframework.security.web.server.util.matcher.ServerWebExchangeMatcher.MatchResult;
import org.springframework.web.server.WebFilterChain;
import org.springframework.web.server.WebSession;
import reactor.core.publisher.Mono;
@@ -33,9 +37,11 @@ import reactor.test.publisher.PublisherProbe;
import static org.assertj.core.api.AssertionsForInterfaceTypes.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.when;
import static org.springframework.mock.web.server.MockServerWebExchange.from;
/**
* @author Rob Winch
* @author Parikshit Dutta
* @since 5.0
*/
@RunWith(MockitoJUnitRunner.class)
@@ -49,10 +55,10 @@ public class CsrfWebFilterTests {
private CsrfWebFilter csrfFilter = new CsrfWebFilter();
private MockServerWebExchange get = MockServerWebExchange.from(
private MockServerWebExchange get = from(
MockServerHttpRequest.get("/"));
private MockServerWebExchange post = MockServerWebExchange.from(
private MockServerWebExchange post = from(
MockServerHttpRequest.post("/"));
@Test
@@ -104,7 +110,7 @@ public class CsrfWebFilterTests {
this.csrfFilter.setCsrfTokenRepository(this.repository);
when(this.repository.loadToken(any()))
.thenReturn(Mono.just(this.token));
this.post = MockServerWebExchange.from(MockServerHttpRequest.post("/")
this.post = from(MockServerHttpRequest.post("/")
.body(this.token.getParameterName() + "="+this.token.getToken()+"INVALID"));
Mono<Void> result = this.csrfFilter.filter(this.post, this.chain);
@@ -125,7 +131,7 @@ public class CsrfWebFilterTests {
.thenReturn(Mono.just(this.token));
when(this.repository.generateToken(any()))
.thenReturn(Mono.just(this.token));
this.post = MockServerWebExchange.from(MockServerHttpRequest.post("/")
this.post = from(MockServerHttpRequest.post("/")
.contentType(MediaType.APPLICATION_FORM_URLENCODED)
.body(this.token.getParameterName() + "="+this.token.getToken()));
@@ -142,7 +148,7 @@ public class CsrfWebFilterTests {
this.csrfFilter.setCsrfTokenRepository(this.repository);
when(this.repository.loadToken(any()))
.thenReturn(Mono.just(this.token));
this.post = MockServerWebExchange.from(MockServerHttpRequest.post("/")
this.post = from(MockServerHttpRequest.post("/")
.header(this.token.getHeaderName(), this.token.getToken()+"INVALID"));
Mono<Void> result = this.csrfFilter.filter(this.post, this.chain);
@@ -163,7 +169,7 @@ public class CsrfWebFilterTests {
.thenReturn(Mono.just(this.token));
when(this.repository.generateToken(any()))
.thenReturn(Mono.just(this.token));
this.post = MockServerWebExchange.from(MockServerHttpRequest.post("/")
this.post = from(MockServerHttpRequest.post("/")
.header(this.token.getHeaderName(), this.token.getToken()));
Mono<Void> result = this.csrfFilter.filter(this.post, this.chain);
@@ -173,4 +179,14 @@ public class CsrfWebFilterTests {
chainResult.assertWasSubscribed();
}
@Test
// gh-8452
public void matchesRequireCsrfProtectionWhenNonStandardHTTPMethodIsUsed() {
HttpMethod customHttpMethod = HttpMethod.resolve("non-standard-http-method");
MockServerWebExchange nonStandardHttpRequest = from(MockServerHttpRequest.method(customHttpMethod, "/"));
ServerWebExchangeMatcher serverWebExchangeMatcher = CsrfWebFilter.DEFAULT_CSRF_MATCHER;
assertThat(serverWebExchangeMatcher.matches(nonStandardHttpRequest).map(MatchResult::isMatch).block()).isTrue();
}
}