From ee52539c13f366c5e8da4bc8abdf1b2166d965b8 Mon Sep 17 00:00:00 2001 From: Gary Russell Date: Thu, 18 Jun 2020 15:38:08 -0400 Subject: [PATCH] Use allowed list for trusted deserialization --- .../springframework/amqp/core/Message.java | 10 ++++---- ...wedListDeserializingMessageConverter.java} | 24 +++++++++---------- .../support/converter/DefaultClassMapper.java | 6 ++--- .../DefaultJackson2JavaTypeMapper.java | 6 ++--- .../converter/SerializerMessageConverter.java | 6 ++--- .../converter/SimpleMessageConverter.java | 4 ++-- .../amqp/utils/SerializationUtils.java | 16 ++++++------- .../amqp/core/MessageTests.java | 2 +- ...stDeserializingMessageConverterTests.java} | 12 +++++----- .../SerializerMessageConverterTests.java | 2 +- .../SimpleMessageConverterTests.java | 2 +- src/reference/asciidoc/amqp.adoc | 6 ++--- src/reference/asciidoc/appendix.adoc | 4 ++-- 13 files changed, 50 insertions(+), 50 deletions(-) rename spring-amqp/src/main/java/org/springframework/amqp/support/converter/{WhiteListDeserializingMessageConverter.java => AllowedListDeserializingMessageConverter.java} (66%) rename spring-amqp/src/test/java/org/springframework/amqp/support/converter/{WhiteListDeserializingMessageConverterTests.java => AllowedListDeserializingMessageConverterTests.java} (84%) diff --git a/spring-amqp/src/main/java/org/springframework/amqp/core/Message.java b/spring-amqp/src/main/java/org/springframework/amqp/core/Message.java index a53d9fb1..ba2c1237 100644 --- a/spring-amqp/src/main/java/org/springframework/amqp/core/Message.java +++ b/spring-amqp/src/main/java/org/springframework/amqp/core/Message.java @@ -48,7 +48,7 @@ public class Message implements Serializable { private static final String DEFAULT_ENCODING = Charset.defaultCharset().name(); - private static final Set whiteListPatterns = // NOSONAR lower case static + private static final Set 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); diff --git a/spring-amqp/src/main/java/org/springframework/amqp/support/converter/WhiteListDeserializingMessageConverter.java b/spring-amqp/src/main/java/org/springframework/amqp/support/converter/AllowedListDeserializingMessageConverter.java similarity index 66% rename from spring-amqp/src/main/java/org/springframework/amqp/support/converter/WhiteListDeserializingMessageConverter.java rename to spring-amqp/src/main/java/org/springframework/amqp/support/converter/AllowedListDeserializingMessageConverter.java index 2018991c..14d6247d 100644 --- a/spring-amqp/src/main/java/org/springframework/amqp/support/converter/WhiteListDeserializingMessageConverter.java +++ b/spring-amqp/src/main/java/org/springframework/amqp/support/converter/AllowedListDeserializingMessageConverter.java @@ -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 whiteListPatterns = new LinkedHashSet(); + private final Set allowedListPatterns = new LinkedHashSet(); /** * 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 whiteListPatterns) { - this.whiteListPatterns.clear(); - this.whiteListPatterns.addAll(whiteListPatterns); + public void setAllowedListPatterns(List 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); } } diff --git a/spring-amqp/src/main/java/org/springframework/amqp/support/converter/DefaultClassMapper.java b/spring-amqp/src/main/java/org/springframework/amqp/support/converter/DefaultClassMapper.java index 626ece4b..3f5c9cf3 100644 --- a/spring-amqp/src/main/java/org/springframework/amqp/support/converter/DefaultClassMapper.java +++ b/spring-amqp/src/main/java/org/springframework/amqp/support/converter/DefaultClassMapper.java @@ -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); } } } diff --git a/spring-amqp/src/main/java/org/springframework/amqp/support/converter/DefaultJackson2JavaTypeMapper.java b/spring-amqp/src/main/java/org/springframework/amqp/support/converter/DefaultJackson2JavaTypeMapper.java index f2f8ad5d..8de76426 100644 --- a/spring-amqp/src/main/java/org/springframework/amqp/support/converter/DefaultJackson2JavaTypeMapper.java +++ b/spring-amqp/src/main/java/org/springframework/amqp/support/converter/DefaultJackson2JavaTypeMapper.java @@ -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); } } } diff --git a/spring-amqp/src/main/java/org/springframework/amqp/support/converter/SerializerMessageConverter.java b/spring-amqp/src/main/java/org/springframework/amqp/support/converter/SerializerMessageConverter.java index ba1b2fab..5b12f288 100644 --- a/spring-amqp/src/main/java/org/springframework/amqp/support/converter/SerializerMessageConverter.java +++ b/spring-amqp/src/main/java/org/springframework/amqp/support/converter/SerializerMessageConverter.java @@ -41,14 +41,14 @@ import org.springframework.core.serializer.Serializer; * {@link MessageProperties#getContentType() content-type} of the provided Message. *

* 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; } diff --git a/spring-amqp/src/main/java/org/springframework/amqp/support/converter/SimpleMessageConverter.java b/spring-amqp/src/main/java/org/springframework/amqp/support/converter/SimpleMessageConverter.java index fc35824a..5528e68a 100644 --- a/spring-amqp/src/main/java/org/springframework/amqp/support/converter/SimpleMessageConverter.java +++ b/spring-amqp/src/main/java/org/springframework/amqp/support/converter/SimpleMessageConverter.java @@ -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; } diff --git a/spring-amqp/src/main/java/org/springframework/amqp/utils/SerializationUtils.java b/spring-amqp/src/main/java/org/springframework/amqp/utils/SerializationUtils.java index 598950e9..ccdb9ce6 100644 --- a/spring-amqp/src/main/java/org/springframework/amqp/utils/SerializationUtils.java +++ b/spring-amqp/src/main/java/org/springframework/amqp/utils/SerializationUtils.java @@ -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 whiteListPatterns, ClassLoader classLoader) + public static Object deserialize(InputStream inputStream, Set 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 whiteListPatterns) { - if (ObjectUtils.isEmpty(whiteListPatterns)) { + public static void checkAllowedList(Class clazz, Set 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; } diff --git a/spring-amqp/src/test/java/org/springframework/amqp/core/MessageTests.java b/spring-amqp/src/test/java/org/springframework/amqp/core/MessageTests.java index 91b320c8..2b12995e 100644 --- a/spring-amqp/src/test/java/org/springframework/amqp/core/MessageTests.java +++ b/spring-amqp/src/test/java/org/springframework/amqp/core/MessageTests.java @@ -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"); } diff --git a/spring-amqp/src/test/java/org/springframework/amqp/support/converter/WhiteListDeserializingMessageConverterTests.java b/spring-amqp/src/test/java/org/springframework/amqp/support/converter/AllowedListDeserializingMessageConverterTests.java similarity index 84% rename from spring-amqp/src/test/java/org/springframework/amqp/support/converter/WhiteListDeserializingMessageConverterTests.java rename to spring-amqp/src/test/java/org/springframework/amqp/support/converter/AllowedListDeserializingMessageConverterTests.java index 52516df7..dfb3d170 100644 --- a/spring-amqp/src/test/java/org/springframework/amqp/support/converter/WhiteListDeserializingMessageConverterTests.java +++ b/spring-amqp/src/test/java/org/springframework/amqp/support/converter/AllowedListDeserializingMessageConverterTests.java @@ -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"); diff --git a/spring-amqp/src/test/java/org/springframework/amqp/support/converter/SerializerMessageConverterTests.java b/spring-amqp/src/test/java/org/springframework/amqp/support/converter/SerializerMessageConverterTests.java index 2ca78089..74eb71cf 100644 --- a/spring-amqp/src/test/java/org/springframework/amqp/support/converter/SerializerMessageConverterTests.java +++ b/spring-amqp/src/test/java/org/springframework/amqp/support/converter/SerializerMessageConverterTests.java @@ -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 { diff --git a/spring-amqp/src/test/java/org/springframework/amqp/support/converter/SimpleMessageConverterTests.java b/spring-amqp/src/test/java/org/springframework/amqp/support/converter/SimpleMessageConverterTests.java index 667e7ea6..ec6e72d9 100644 --- a/spring-amqp/src/test/java/org/springframework/amqp/support/converter/SimpleMessageConverterTests.java +++ b/spring-amqp/src/test/java/org/springframework/amqp/support/converter/SimpleMessageConverterTests.java @@ -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 { diff --git a/src/reference/asciidoc/amqp.adoc b/src/reference/asciidoc/amqp.adoc index 0eee4c73..e164e265 100644 --- a/src/reference/asciidoc/amqp.adoc +++ b/src/reference/asciidoc/amqp.adoc @@ -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[]` 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]] diff --git a/src/reference/asciidoc/appendix.adoc b/src/reference/asciidoc/appendix.adoc index 9b43c393..4f46209f 100644 --- a/src/reference/asciidoc/appendix.adoc +++ b/src/reference/asciidoc/appendix.adoc @@ -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 <> for more information.