Use allowed list for trusted deserialization
This commit is contained in:
committed by
Artem Bilan
parent
8b0a351f8e
commit
ee52539c13
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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");
|
||||
}
|
||||
|
||||
@@ -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");
|
||||
@@ -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 {
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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]]
|
||||
|
||||
@@ -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.
|
||||
|
||||
|
||||
Reference in New Issue
Block a user