Use allowed list for trusted deserialization

This commit is contained in:
Gary Russell
2020-06-18 15:38:08 -04:00
committed by Artem Bilan
parent 8b0a351f8e
commit ee52539c13
13 changed files with 50 additions and 50 deletions

View File

@@ -48,7 +48,7 @@ public class Message implements Serializable {
private static final String DEFAULT_ENCODING = Charset.defaultCharset().name();
private static final Set<String> whiteListPatterns = // NOSONAR lower case static
private static final Set<String> ALLOWED_LIST_PATTERNS =
new LinkedHashSet<>(Arrays.asList("java.util.*", "java.lang.*"));
private static String bodyEncoding = DEFAULT_ENCODING;
@@ -63,7 +63,7 @@ public class Message implements Serializable {
}
/**
* Add patterns to the white list of permissable package/class name patterns for
* Add patterns to the allowed list of permissible package/class name patterns for
* deserialization in {@link #toString()}.
* The patterns will be applied in order until a match is found.
* A class can be fully qualified or a wildcard '*' is allowed at the
@@ -74,9 +74,9 @@ public class Message implements Serializable {
* @param patterns the patterns.
* @since 1.5.7
*/
public static void addWhiteListPatterns(String... patterns) {
public static void addAllowedListPatterns(String... patterns) {
Assert.notNull(patterns, "'patterns' cannot be null");
whiteListPatterns.addAll(Arrays.asList(patterns));
ALLOWED_LIST_PATTERNS.addAll(Arrays.asList(patterns));
}
/**
@@ -118,7 +118,7 @@ public class Message implements Serializable {
boolean nullProps = this.messageProperties == null;
String contentType = nullProps ? null : this.messageProperties.getContentType();
if (MessageProperties.CONTENT_TYPE_SERIALIZED_OBJECT.equals(contentType)) {
return SerializationUtils.deserialize(new ByteArrayInputStream(this.body), whiteListPatterns,
return SerializationUtils.deserialize(new ByteArrayInputStream(this.body), ALLOWED_LIST_PATTERNS,
ClassUtils.getDefaultClassLoader()).toString();
}
String encoding = encoding(nullProps);

View File

@@ -30,9 +30,9 @@ import org.springframework.amqp.utils.SerializationUtils;
* @since 1.5.5
*
*/
public abstract class WhiteListDeserializingMessageConverter extends AbstractMessageConverter {
public abstract class AllowedListDeserializingMessageConverter extends AbstractMessageConverter {
private final Set<String> whiteListPatterns = new LinkedHashSet<String>();
private final Set<String> allowedListPatterns = new LinkedHashSet<String>();
/**
* Set simple patterns for allowable packages/classes for deserialization.
@@ -40,25 +40,25 @@ public abstract class WhiteListDeserializingMessageConverter extends AbstractMes
* A class can be fully qualified or a wildcard '*' is allowed at the
* beginning or end of the class name.
* Examples: {@code com.foo.*}, {@code *.MyClass}.
* @param whiteListPatterns the patterns.
* @param patterns the patterns.
*/
public void setWhiteListPatterns(List<String> whiteListPatterns) {
this.whiteListPatterns.clear();
this.whiteListPatterns.addAll(whiteListPatterns);
public void setAllowedListPatterns(List<String> patterns) {
this.allowedListPatterns.clear();
this.allowedListPatterns.addAll(patterns);
}
/**
* Add package/class patterns to the white list.
* Add package/class patterns to the allowed list.
* @param patterns the patterns to add.
* @since 1.5.7
* @see #setWhiteListPatterns(List)
* @see #setAllowedListPatterns(List)
*/
public void addWhiteListPatterns(String... patterns) {
Collections.addAll(this.whiteListPatterns, patterns);
public void addAllowedListPatterns(String... patterns) {
Collections.addAll(this.allowedListPatterns, patterns);
}
protected void checkWhiteList(Class<?> clazz) {
SerializationUtils.checkWhiteList(clazz, this.whiteListPatterns);
protected void checkAllowedList(Class<?> clazz) {
SerializationUtils.checkAllowedList(clazz, this.allowedListPatterns);
}
}

View File

@@ -119,13 +119,13 @@ public class DefaultClassMapper implements ClassMapper, InitializingBean {
*/
public void setTrustedPackages(@Nullable String... trustedPackages) {
if (trustedPackages != null) {
for (String whiteListClass : trustedPackages) {
if ("*".equals(whiteListClass)) {
for (String trusted : trustedPackages) {
if ("*".equals(trusted)) {
this.trustedPackages.clear();
break;
}
else {
this.trustedPackages.add(whiteListClass);
this.trustedPackages.add(trusted);
}
}
}

View File

@@ -92,13 +92,13 @@ public class DefaultJackson2JavaTypeMapper extends AbstractJavaTypeMapper implem
*/
public void setTrustedPackages(@Nullable String... trustedPackages) {
if (trustedPackages != null) {
for (String whiteListClass : trustedPackages) {
if ("*".equals(whiteListClass)) {
for (String trusted : trustedPackages) {
if ("*".equals(trusted)) {
this.trustedPackages.clear();
break;
}
else {
this.trustedPackages.add(whiteListClass);
this.trustedPackages.add(trusted);
}
}
}

View File

@@ -41,14 +41,14 @@ import org.springframework.core.serializer.Serializer;
* {@link MessageProperties#getContentType() content-type} of the provided Message.
* <p>
* If a {@link DefaultDeserializer} is configured (default),
* the {@link #setWhiteListPatterns(java.util.List) white list patterns} will be applied
* the {@link #setAllowedListPatterns(java.util.List) allowed patterns} will be applied
* (if configured); for all other deserializers, the deserializer is responsible for
* checking classes, if necessary.
*
* @author Dave Syer
* @author Gary Russell
*/
public class SerializerMessageConverter extends WhiteListDeserializingMessageConverter {
public class SerializerMessageConverter extends AllowedListDeserializingMessageConverter {
public static final String DEFAULT_CHARSET = "UTF-8";
@@ -174,7 +174,7 @@ public class SerializerMessageConverter extends WhiteListDeserializingMessageCon
protected Class<?> resolveClass(ObjectStreamClass classDesc)
throws IOException, ClassNotFoundException {
Class<?> clazz = super.resolveClass(classDesc);
checkWhiteList(clazz);
checkAllowedList(clazz);
return clazz;
}

View File

@@ -42,7 +42,7 @@ import org.springframework.util.ClassUtils;
* @author Oleg Zhurakousky
* @author Gary Russell
*/
public class SimpleMessageConverter extends WhiteListDeserializingMessageConverter implements BeanClassLoaderAware {
public class SimpleMessageConverter extends AllowedListDeserializingMessageConverter implements BeanClassLoaderAware {
public static final String DEFAULT_CHARSET = "UTF-8";
@@ -176,7 +176,7 @@ public class SimpleMessageConverter extends WhiteListDeserializingMessageConvert
@Override
protected Class<?> resolveClass(ObjectStreamClass classDesc) throws IOException, ClassNotFoundException {
Class<?> clazz = super.resolveClass(classDesc);
checkWhiteList(clazz);
checkAllowedList(clazz);
return clazz;
}

View File

@@ -101,13 +101,13 @@ public final class SerializationUtils {
/**
* Deserialize the stream.
* @param inputStream the stream.
* @param whiteListPatterns allowed classes.
* @param allowedListPatterns allowed classes.
* @param classLoader the class loader.
* @return the result.
* @throws IOException IO Exception.
* @since 2.1
*/
public static Object deserialize(InputStream inputStream, Set<String> whiteListPatterns, ClassLoader classLoader)
public static Object deserialize(InputStream inputStream, Set<String> allowedListPatterns, ClassLoader classLoader)
throws IOException {
try (
@@ -117,7 +117,7 @@ public final class SerializationUtils {
protected Class<?> resolveClass(ObjectStreamClass classDesc)
throws IOException, ClassNotFoundException {
Class<?> clazz = super.resolveClass(classDesc);
checkWhiteList(clazz, whiteListPatterns);
checkAllowedList(clazz, allowedListPatterns);
return clazz;
}
@@ -131,13 +131,13 @@ public final class SerializationUtils {
}
/**
* Verify that the class is in the white list.
* Verify that the class is in the allowed list.
* @param clazz the class.
* @param whiteListPatterns the patterns.
* @param patterns the patterns.
* @since 2.1
*/
public static void checkWhiteList(Class<?> clazz, Set<String> whiteListPatterns) {
if (ObjectUtils.isEmpty(whiteListPatterns)) {
public static void checkAllowedList(Class<?> clazz, Set<String> patterns) {
if (ObjectUtils.isEmpty(patterns)) {
return;
}
if (clazz.isArray() || clazz.isPrimitive() || clazz.equals(String.class)
@@ -145,7 +145,7 @@ public final class SerializationUtils {
return;
}
String className = clazz.getName();
for (String pattern : whiteListPatterns) {
for (String pattern : patterns) {
if (PatternMatchUtils.simpleMatch(pattern, className)) {
return;
}

View File

@@ -106,7 +106,7 @@ public class MessageTests {
Message listMessage = new SimpleMessageConverter().toMessage(Collections.singletonList(new Foo()),
new MessageProperties());
assertThat(listMessage.toString()).doesNotContainPattern("aFoo");
Message.addWhiteListPatterns(Foo.class.getName());
Message.addAllowedListPatterns(Foo.class.getName());
assertThat(message.toString()).contains("aFoo");
assertThat(listMessage.toString()).contains("aFoo");
}

View File

@@ -33,29 +33,29 @@ import org.springframework.util.Assert;
* @since 1.5.5
*
*/
public class WhiteListDeserializingMessageConverterTests {
public class AllowedListDeserializingMessageConverterTests {
@Test
public void testWhiteList() throws Exception {
public void testAllowedList() throws Exception {
SerializerMessageConverter converter = new SerializerMessageConverter();
TestBean testBean = new TestBean("foo");
Message message = converter.toMessage(testBean, new MessageProperties());
Object fromMessage = converter.fromMessage(message);
assertThat(fromMessage).isEqualTo(testBean);
converter.setWhiteListPatterns(Collections.singletonList("*"));
converter.setAllowedListPatterns(Collections.singletonList("*"));
fromMessage = converter.fromMessage(message);
assertThat(fromMessage).isEqualTo(testBean);
converter.setWhiteListPatterns(Collections.singletonList("org.springframework.amqp.*"));
converter.setAllowedListPatterns(Collections.singletonList("org.springframework.amqp.*"));
fromMessage = converter.fromMessage(message);
assertThat(fromMessage).isEqualTo(testBean);
converter.setWhiteListPatterns(Collections.singletonList("*$TestBean"));
converter.setAllowedListPatterns(Collections.singletonList("*$TestBean"));
fromMessage = converter.fromMessage(message);
assertThat(fromMessage).isEqualTo(testBean);
try {
converter.setWhiteListPatterns(Collections.singletonList("foo.*"));
converter.setAllowedListPatterns(Collections.singletonList("foo.*"));
fromMessage = converter.fromMessage(message);
assertThat(fromMessage).isEqualTo(testBean);
fail("Expected SecurityException");

View File

@@ -41,7 +41,7 @@ import org.springframework.core.serializer.Deserializer;
* @author Mark Fisher
* @author Gary Russell
*/
public class SerializerMessageConverterTests extends WhiteListDeserializingMessageConverterTests {
public class SerializerMessageConverterTests extends AllowedListDeserializingMessageConverterTests {
@Test
public void bytesAsDefaultMessageBodyType() throws Exception {

View File

@@ -34,7 +34,7 @@ import org.springframework.amqp.core.MessageProperties;
* @author Mark Fisher
* @author Gary Russell
*/
public class SimpleMessageConverterTests extends WhiteListDeserializingMessageConverterTests {
public class SimpleMessageConverterTests extends AllowedListDeserializingMessageConverterTests {
@Test
public void bytesAsDefaultMessageBodyType() throws Exception {

View File

@@ -57,7 +57,7 @@ You can also extend those properties with user-defined 'headers' by calling the
IMPORTANT: Starting with versions `1.5.7`, `1.6.11`, `1.7.4`, and `2.0.0`, if a message body is a serialized `Serializable` java object, it is no longer deserialized (by default) when performing `toString()` operations (such as in log messages).
This is to prevent unsafe deserialization.
By default, only `java.util` and `java.lang` classes are deserialized.
To revert to the previous behavior, you can add allowable class/package patterns by invoking `Message.addWhiteListPatterns(...)`.
To revert to the previous behavior, you can add allowable class/package patterns by invoking `Message.addAllowedListPatterns(...)`.
A simple `*` wildcard is supported, for example `com.something.*, *.MyClass`.
Bodies that cannot be deserialized are represented by `byte[<size>]` in log messages.
@@ -3753,14 +3753,14 @@ consider configuring which packages and classes are allowed to be deserialized.
This applies to both the `SimpleMessageConverter` and `SerializerMessageConverter` when it is configured to use a
`DefaultDeserializer` either implicitly or via configuration.
By default, the white list is empty, meaning all classes are deserialized.
By default, the allowed list is empty, meaning all classes are deserialized.
You can set a list of patterns, such as `thing1.*`, `thing1.thing2.Cat` or `*.MySafeClass`.
The patterns are checked in order until a match is found.
If there is no match, a `SecurityException` is thrown.
You can set the patterns using the `whiteListPatterns` property on these converters.
You can set the patterns using the `allowedListPatterns` property on these converters.
====
[[message-properties-converters]]

View File

@@ -693,8 +693,8 @@ factory.
===== Java Deserialization
You can now configure a "`white list`" of allowable classes when you use Java deserialization.
You should consider creating a white list if you accept messages with serialized java objects from
You can now configure a "`allowed list`" of allowable classes when you use Java deserialization.
You should consider creating an allowed list if you accept messages with serialized java objects from
untrusted sources.
See <<java-deserialization>> for more information.