Replace whitelist with allowlist

Issue gh-8676
This commit is contained in:
Rob Winch 2020-06-10 10:08:31 -05:00
parent a907026eae
commit ca1252be94
9 changed files with 51 additions and 47 deletions

View File

@ -2,7 +2,7 @@ buildscript {
dependencies {
classpath 'io.spring.gradle:spring-build-conventions:0.0.32.RELEASE'
classpath "org.springframework.boot:spring-boot-gradle-plugin:$springBootVersion"
classpath 'io.spring.nohttp:nohttp-gradle:0.0.2.RELEASE'
classpath 'io.spring.nohttp:nohttp-gradle:0.0.5.RELEASE'
classpath "io.freefair.gradle:aspectj-plugin:5.0.1"
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlinVersion"
}
@ -39,3 +39,7 @@ subprojects {
options.encoding = "UTF-8"
}
}
nohttp {
allowlistFile = project.file("etc/nohttp/allowlist.lines")
}

View File

@ -90,7 +90,7 @@ public final class SecurityJackson2Modules {
if (mapper != null) {
TypeResolverBuilder<?> typeBuilder = mapper.getDeserializationConfig().getDefaultTyper(null);
if (typeBuilder == null) {
mapper.setDefaultTyping(createWhitelistedDefaultTyping());
mapper.setDefaultTyping(createAllowlistedDefaultTyping());
}
}
}
@ -148,11 +148,11 @@ public final class SecurityJackson2Modules {
}
/**
* Creates a TypeResolverBuilder that performs whitelisting.
* @return a TypeResolverBuilder that performs whitelisting.
* Creates a TypeResolverBuilder that restricts allowed types.
* @return a TypeResolverBuilder that restricts allowed types.
*/
private static TypeResolverBuilder<? extends TypeResolverBuilder> createWhitelistedDefaultTyping() {
TypeResolverBuilder<? extends TypeResolverBuilder> result = new WhitelistTypeResolverBuilder(ObjectMapper.DefaultTyping.NON_FINAL);
private static TypeResolverBuilder<? extends TypeResolverBuilder> createAllowlistedDefaultTyping() {
TypeResolverBuilder<? extends TypeResolverBuilder> result = new AllowlistTypeResolverBuilder(ObjectMapper.DefaultTyping.NON_FINAL);
result = result.init(JsonTypeInfo.Id.CLASS, null);
result = result.inclusion(JsonTypeInfo.As.PROPERTY);
return result;
@ -164,9 +164,9 @@ public final class SecurityJackson2Modules {
* and overrides the {@code TypeIdResolver}
* @author Rob Winch
*/
static class WhitelistTypeResolverBuilder extends ObjectMapper.DefaultTypeResolverBuilder {
static class AllowlistTypeResolverBuilder extends ObjectMapper.DefaultTypeResolverBuilder {
WhitelistTypeResolverBuilder(ObjectMapper.DefaultTyping defaultTyping) {
AllowlistTypeResolverBuilder(ObjectMapper.DefaultTyping defaultTyping) {
super(
defaultTyping,
//we do explicit validation in the TypeIdResolver
@ -182,17 +182,17 @@ public final class SecurityJackson2Modules {
PolymorphicTypeValidator subtypeValidator,
Collection<NamedType> subtypes, boolean forSer, boolean forDeser) {
TypeIdResolver result = super.idResolver(config, baseType, subtypeValidator, subtypes, forSer, forDeser);
return new WhitelistTypeIdResolver(result);
return new AllowlistTypeIdResolver(result);
}
}
/**
* A {@link TypeIdResolver} that delegates to an existing implementation and throws an IllegalStateException if the
* class being looked up is not whitelisted, does not provide an explicit mixin, and is not annotated with Jackson
* class being looked up is not in the allowlist, does not provide an explicit mixin, and is not annotated with Jackson
* mappings. See https://github.com/spring-projects/spring-security/issues/4370
*/
static class WhitelistTypeIdResolver implements TypeIdResolver {
private static final Set<String> WHITELIST_CLASS_NAMES = Collections.unmodifiableSet(new HashSet(Arrays.asList(
static class AllowlistTypeIdResolver implements TypeIdResolver {
private static final Set<String> ALLOWLIST_CLASS_NAMES = Collections.unmodifiableSet(new HashSet(Arrays.asList(
"java.util.ArrayList",
"java.util.Collections$EmptyList",
"java.util.Collections$EmptyMap",
@ -209,7 +209,7 @@ public final class SecurityJackson2Modules {
private final TypeIdResolver delegate;
WhitelistTypeIdResolver(TypeIdResolver delegate) {
AllowlistTypeIdResolver(TypeIdResolver delegate) {
this.delegate = delegate;
}
@ -238,7 +238,7 @@ public final class SecurityJackson2Modules {
DeserializationConfig config = (DeserializationConfig) context.getConfig();
JavaType result = delegate.typeFromId(context, id);
String className = result.getRawClass().getName();
if (isWhitelisted(className)) {
if (isInAllowlist(className)) {
return result;
}
boolean isExplicitMixin = config.findMixInClassFor(result.getRawClass()) != null;
@ -249,14 +249,14 @@ public final class SecurityJackson2Modules {
if (jacksonAnnotation != null) {
return result;
}
throw new IllegalArgumentException("The class with " + id + " and name of " + className + " is not whitelisted. " +
throw new IllegalArgumentException("The class with " + id + " and name of " + className + " is not in the allowlist. " +
"If you believe this class is safe to deserialize, please provide an explicit mapping using Jackson annotations or by providing a Mixin. " +
"If the serialization is only done by a trusted source, you can also enable default typing. " +
"See https://github.com/spring-projects/spring-security/issues/4370 for details");
}
private boolean isWhitelisted(String id) {
return WHITELIST_CLASS_NAMES.contains(id);
private boolean isInAllowlist(String id) {
return ALLOWLIST_CLASS_NAMES.contains(id);
}
@Override

View File

@ -44,20 +44,20 @@ public class SecurityJackson2ModulesTests {
}
@Test
public void readValueWhenNotWhitelistedOrMappedThenThrowsException() {
String content = "{\"@class\":\"org.springframework.security.jackson2.SecurityJackson2ModulesTests$NotWhitelisted\",\"property\":\"bar\"}";
public void readValueWhenNotAllowedOrMappedThenThrowsException() {
String content = "{\"@class\":\"org.springframework.security.jackson2.SecurityJackson2ModulesTests$NotAllowlisted\",\"property\":\"bar\"}";
assertThatThrownBy(() -> {
mapper.readValue(content, Object.class);
}
).hasStackTraceContaining("whitelisted");
).hasStackTraceContaining("allowlist");
}
@Test
public void readValueWhenExplicitDefaultTypingAfterSecuritySetupThenReadsAsSpecificType() throws Exception {
mapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL, JsonTypeInfo.As.PROPERTY);
String content = "{\"@class\":\"org.springframework.security.jackson2.SecurityJackson2ModulesTests$NotWhitelisted\",\"property\":\"bar\"}";
String content = "{\"@class\":\"org.springframework.security.jackson2.SecurityJackson2ModulesTests$NotAllowlisted\",\"property\":\"bar\"}";
assertThat(mapper.readValue(content, Object.class)).isInstanceOf(NotWhitelisted.class);
assertThat(mapper.readValue(content, Object.class)).isInstanceOf(NotAllowlisted.class);
}
@Test
@ -65,29 +65,29 @@ public class SecurityJackson2ModulesTests {
mapper = new ObjectMapper();
mapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL, JsonTypeInfo.As.PROPERTY);
SecurityJackson2Modules.enableDefaultTyping(mapper);
String content = "{\"@class\":\"org.springframework.security.jackson2.SecurityJackson2ModulesTests$NotWhitelisted\",\"property\":\"bar\"}";
String content = "{\"@class\":\"org.springframework.security.jackson2.SecurityJackson2ModulesTests$NotAllowlisted\",\"property\":\"bar\"}";
assertThat(mapper.readValue(content, Object.class)).isInstanceOf(NotWhitelisted.class);
assertThat(mapper.readValue(content, Object.class)).isInstanceOf(NotAllowlisted.class);
}
@Test
public void readValueWhenAnnotatedThenReadsAsSpecificType() throws Exception {
String content = "{\"@class\":\"org.springframework.security.jackson2.SecurityJackson2ModulesTests$NotWhitelistedButAnnotated\",\"property\":\"bar\"}";
String content = "{\"@class\":\"org.springframework.security.jackson2.SecurityJackson2ModulesTests$NotAllowlistedButAnnotated\",\"property\":\"bar\"}";
assertThat(mapper.readValue(content, Object.class)).isInstanceOf(NotWhitelistedButAnnotated.class);
assertThat(mapper.readValue(content, Object.class)).isInstanceOf(NotAllowlistedButAnnotated.class);
}
@Test
public void readValueWhenMixinProvidedThenReadsAsSpecificType() throws Exception {
mapper.addMixIn(NotWhitelisted.class, NotWhitelistedMixin.class);
String content = "{\"@class\":\"org.springframework.security.jackson2.SecurityJackson2ModulesTests$NotWhitelisted\",\"property\":\"bar\"}";
mapper.addMixIn(NotAllowlisted.class, NotAllowlistedMixin.class);
String content = "{\"@class\":\"org.springframework.security.jackson2.SecurityJackson2ModulesTests$NotAllowlisted\",\"property\":\"bar\"}";
assertThat(mapper.readValue(content, Object.class)).isInstanceOf(NotWhitelisted.class);
assertThat(mapper.readValue(content, Object.class)).isInstanceOf(NotAllowlisted.class);
}
@Test
public void readValueWhenHashMapThenReadsAsSpecificType() throws Exception {
mapper.addMixIn(NotWhitelisted.class, NotWhitelistedMixin.class);
mapper.addMixIn(NotAllowlisted.class, NotAllowlistedMixin.class);
String content = "{\"@class\":\"java.util.HashMap\"}";
assertThat(mapper.readValue(content, Object.class)).isInstanceOf(HashMap.class);
@ -99,7 +99,7 @@ public class SecurityJackson2ModulesTests {
public @interface NotJacksonAnnotation {}
@NotJacksonAnnotation
static class NotWhitelisted {
static class NotAllowlisted {
private String property = "bar";
public String getProperty() {
@ -111,7 +111,7 @@ public class SecurityJackson2ModulesTests {
}
@JsonIgnoreType(false)
static class NotWhitelistedButAnnotated {
static class NotAllowlistedButAnnotated {
private String property = "bar";
public String getProperty() {
@ -126,7 +126,7 @@ public class SecurityJackson2ModulesTests {
@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY, getterVisibility = JsonAutoDetect.Visibility.NONE,
isGetterVisibility = JsonAutoDetect.Visibility.NONE)
@JsonIgnoreProperties(ignoreUnknown = true)
abstract class NotWhitelistedMixin {
abstract class NotAllowlistedMixin {
}
}

View File

@ -1082,7 +1082,7 @@ In this case, you construct `JwtIssuerReactiveAuthenticationManagerResolver` wit
This approach allows us to add and remove elements from the repository (shown as a `Map` in the snippet) at runtime.
NOTE: It would be unsafe to simply take any issuer and construct an `ReactiveAuthenticationManager` from it.
The issuer should be one that the code can verify from a trusted source like a whitelist.
The issuer should be one that the code can verify from a trusted source like an allowed list of issuers.
[[webflux-oauth2resourceserver-bearertoken-resolver]]
== Bearer Token Resolution

View File

@ -1857,7 +1857,7 @@ In this case, you construct `JwtIssuerAuthenticationManagerResolver` with a stra
This approach allows us to add and remove elements from the repository (shown as a `Map` in the snippet) at runtime.
NOTE: It would be unsafe to simply take any issuer and construct an `AuthenticationManager` from it.
The issuer should be one that the code can verify from a trusted source like a whitelist.
The issuer should be one that the code can verify from a trusted source like a list of allowed issuers.
===== Parsing the Claim Only Once
@ -1907,7 +1907,7 @@ public class TenantJWSKeySelector
----
<1> A hypothetical source for tenant information
<2> A cache for `JWKKeySelector`s, keyed by tenant identifier
<3> Looking up the tenant is more secure than simply calculating the JWK Set endpoint on the fly - the lookup acts as a tenant whitelist
<3> Looking up the tenant is more secure than simply calculating the JWK Set endpoint on the fly - the lookup acts as a list of allowed tenants
<4> Create a `JWSKeySelector` via the types of keys that come back from the JWK Set endpoint - the lazy lookup here means that you don't need to configure all tenants at startup
The above key selector is a composition of many key selectors.

View File

@ -45,7 +45,7 @@ import org.springframework.util.Assert;
*
* To use, this class must be able to determine whether or not the `iss` claim is trusted. Recall that
* anyone can stand up an authorization server and issue valid tokens to a resource server. The simplest way
* to achieve this is to supply a whitelist of trusted issuers in the constructor.
* to achieve this is to supply a list of trusted issuers in the constructor.
*
* This class derives the Issuer from the `iss` claim found in the {@link HttpServletRequest}'s
* <a href="https://tools.ietf.org/html/rfc6750#section-1.2" target="_blank">Bearer Token</a>.
@ -60,7 +60,7 @@ public final class JwtIssuerAuthenticationManagerResolver implements Authenticat
/**
* Construct a {@link JwtIssuerAuthenticationManagerResolver} using the provided parameters
*
* @param trustedIssuers a whitelist of trusted issuers
* @param trustedIssuers a list of trusted issuers
*/
public JwtIssuerAuthenticationManagerResolver(String... trustedIssuers) {
this(Arrays.asList(trustedIssuers));
@ -69,7 +69,7 @@ public final class JwtIssuerAuthenticationManagerResolver implements Authenticat
/**
* Construct a {@link JwtIssuerAuthenticationManagerResolver} using the provided parameters
*
* @param trustedIssuers a whitelist of trusted issuers
* @param trustedIssuers a list of trusted issuers
*/
public JwtIssuerAuthenticationManagerResolver(Collection<String> trustedIssuers) {
Assert.notEmpty(trustedIssuers, "trustedIssuers cannot be empty");
@ -82,7 +82,7 @@ public final class JwtIssuerAuthenticationManagerResolver implements Authenticat
* Construct a {@link JwtIssuerAuthenticationManagerResolver} using the provided parameters
*
* Note that the {@link AuthenticationManagerResolver} provided in this constructor will need to
* verify that the issuer is trusted. This should be done via a whitelist.
* verify that the issuer is trusted. This should be done via an allowlist.
*
* One way to achieve this is with a {@link Map} where the keys are the known issuers:
* <pre>
@ -93,7 +93,7 @@ public final class JwtIssuerAuthenticationManagerResolver implements Authenticat
* (authenticationManagers::get);
* </pre>
*
* The keys in the {@link Map} are the whitelist.
* The keys in the {@link Map} are the allowed issuers.
*
* @param issuerAuthenticationManagerResolver a strategy for resolving the {@link AuthenticationManager} by the issuer
*/

View File

@ -48,7 +48,7 @@ import org.springframework.web.server.ServerWebExchange;
*
* To use, this class must be able to determine whether or not the `iss` claim is trusted. Recall that
* anyone can stand up an authorization server and issue valid tokens to a resource server. The simplest way
* to achieve this is to supply a whitelist of trusted issuers in the constructor.
* to achieve this is to supply a list of trusted issuers in the constructor.
*
* This class derives the Issuer from the `iss` claim found in the {@link ServerWebExchange}'s
* <a href="https://tools.ietf.org/html/rfc6750#section-1.2" target="_blank">Bearer Token</a>.
@ -66,7 +66,7 @@ public final class JwtIssuerReactiveAuthenticationManagerResolver
/**
* Construct a {@link JwtIssuerReactiveAuthenticationManagerResolver} using the provided parameters
*
* @param trustedIssuers a whitelist of trusted issuers
* @param trustedIssuers a list of trusted issuers
*/
public JwtIssuerReactiveAuthenticationManagerResolver(String... trustedIssuers) {
this(Arrays.asList(trustedIssuers));
@ -75,7 +75,7 @@ public final class JwtIssuerReactiveAuthenticationManagerResolver
/**
* Construct a {@link JwtIssuerReactiveAuthenticationManagerResolver} using the provided parameters
*
* @param trustedIssuers a whitelist of trusted issuers
* @param trustedIssuers a collection of trusted issuers
*/
public JwtIssuerReactiveAuthenticationManagerResolver(Collection<String> trustedIssuers) {
Assert.notEmpty(trustedIssuers, "trustedIssuers cannot be empty");
@ -87,7 +87,7 @@ public final class JwtIssuerReactiveAuthenticationManagerResolver
* Construct a {@link JwtIssuerReactiveAuthenticationManagerResolver} using the provided parameters
*
* Note that the {@link ReactiveAuthenticationManagerResolver} provided in this constructor will need to
* verify that the issuer is trusted. This should be done via a whitelist.
* verify that the issuer is trusted. This should be done via an allowed list of issuers.
*
* One way to achieve this is with a {@link Map} where the keys are the known issuers:
* <pre>
@ -98,7 +98,7 @@ public final class JwtIssuerReactiveAuthenticationManagerResolver
* (issuer -> Mono.justOrEmpty(authenticationManagers.get(issuer));
* </pre>
*
* The keys in the {@link Map} are the whitelist.
* The keys in the {@link Map} are the trusted issuers.
*
* @param issuerAuthenticationManagerResolver a strategy for resolving the {@link ReactiveAuthenticationManager}
* by the issuer

View File

@ -358,7 +358,7 @@ public class StrictHttpFirewall implements HttpFirewall {
if (!this.allowedHttpMethods.contains(request.getMethod())) {
throw new RequestRejectedException("The request was rejected because the HTTP method \"" +
request.getMethod() +
"\" was not included within the whitelist " +
"\" was not included within the list of allowed HTTP methods " +
this.allowedHttpMethods);
}
}