Browse Source

Replace whitelist with allowlist

Issue gh-8676
Rob Winch 5 years ago
parent
commit
ca1252be94

+ 5 - 1
build.gradle

@@ -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")
+}

+ 16 - 16
core/src/main/java/org/springframework/security/jackson2/SecurityJackson2Modules.java

@@ -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

+ 16 - 16
core/src/test/java/org/springframework/security/jackson2/SecurityJackson2ModulesTests.java

@@ -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 {
 
 	}
 }

+ 1 - 1
docs/manual/src/docs/asciidoc/_includes/reactive/oauth2/resource-server.adoc

@@ -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

+ 2 - 2
docs/manual/src/docs/asciidoc/_includes/servlet/oauth2/oauth2-resourceserver.adoc

@@ -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.

+ 0 - 0
etc/nohttp/whitelist.lines → etc/nohttp/allowlist.lines


+ 5 - 5
oauth2/oauth2-resource-server/src/main/java/org/springframework/security/oauth2/server/resource/authentication/JwtIssuerAuthenticationManagerResolver.java

@@ -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
 	 */

+ 5 - 5
oauth2/oauth2-resource-server/src/main/java/org/springframework/security/oauth2/server/resource/authentication/JwtIssuerReactiveAuthenticationManagerResolver.java

@@ -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

+ 1 - 1
web/src/main/java/org/springframework/security/web/firewall/StrictHttpFirewall.java

@@ -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);
 		}
 	}