diff --git a/build.gradle b/build.gradle index e9771d7c..faa82c02 100644 --- a/build.gradle +++ b/build.gradle @@ -293,6 +293,8 @@ subprojects { subproject -> if (name ==~ /(testAll)/) { systemProperty 'RUN_LONG_INTEGRATION_TESTS', 'true' } + environment "SPRING_AMQP_DESERIALIZATION_TRUST_ALL", "true" + useJUnitPlatform() } 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 3a36c744..fed5d908 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 @@ -1,5 +1,5 @@ /* - * Copyright 2006-2022 the original author or authors. + * Copyright 2006-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -37,6 +37,17 @@ import org.springframework.util.PatternMatchUtils; */ public final class SerializationUtils { + private static final String TRUST_ALL_ENV = "SPRING_AMQP_DESERIALIZATION_TRUST_ALL"; + + private static final String TRUST_ALL_PROP = "spring.amqp.deserialization.trust.all"; + + private static final boolean TRUST_ALL; + + static { + TRUST_ALL = Boolean.parseBoolean(System.getenv(TRUST_ALL_ENV)) + || Boolean.parseBoolean(System.getProperty(TRUST_ALL_PROP)); + } + private SerializationUtils() { } @@ -136,11 +147,12 @@ public final class SerializationUtils { * @since 2.1 */ public static void checkAllowedList(Class clazz, Set patterns) { - if (ObjectUtils.isEmpty(patterns)) { + if (TRUST_ALL && ObjectUtils.isEmpty(patterns)) { return; } if (clazz.isArray() || clazz.isPrimitive() || clazz.equals(String.class) - || Number.class.isAssignableFrom(clazz)) { + || Number.class.isAssignableFrom(clazz) + || String.class.equals(clazz)) { return; } String className = clazz.getName(); @@ -149,7 +161,10 @@ public final class SerializationUtils { return; } } - throw new SecurityException("Attempt to deserialize unauthorized " + clazz); + throw new SecurityException("Attempt to deserialize unauthorized " + clazz + + "; add allowed class name patterns to the message converter or, if you trust the message orginiator, " + + "set environment variable '" + + TRUST_ALL_ENV + "' or system property '" + TRUST_ALL_PROP + "' to true"); } } diff --git a/spring-amqp/src/test/java/org/springframework/amqp/support/converter/AllowedListDeserializingMessageConverterTests.java b/spring-amqp/src/test/java/org/springframework/amqp/support/converter/AllowedListDeserializingMessageConverterTests.java index dfb3d170..e56a568a 100644 --- a/spring-amqp/src/test/java/org/springframework/amqp/support/converter/AllowedListDeserializingMessageConverterTests.java +++ b/spring-amqp/src/test/java/org/springframework/amqp/support/converter/AllowedListDeserializingMessageConverterTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2016-2019 the original author or authors. + * Copyright 2016-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -17,7 +17,7 @@ package org.springframework.amqp.support.converter; import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.fail; +import static org.assertj.core.api.Assertions.assertThatExceptionOfType; import java.io.Serializable; import java.util.Collections; @@ -40,7 +40,11 @@ public class AllowedListDeserializingMessageConverterTests { SerializerMessageConverter converter = new SerializerMessageConverter(); TestBean testBean = new TestBean("foo"); Message message = converter.toMessage(testBean, new MessageProperties()); - Object fromMessage = converter.fromMessage(message); + // when env var not set +// assertThatExceptionOfType(SecurityException.class).isThrownBy(() -> converter.fromMessage(message)); + Object fromMessage; + // when env var set. + fromMessage = converter.fromMessage(message); assertThat(fromMessage).isEqualTo(testBean); converter.setAllowedListPatterns(Collections.singletonList("*")); @@ -54,15 +58,8 @@ public class AllowedListDeserializingMessageConverterTests { fromMessage = converter.fromMessage(message); assertThat(fromMessage).isEqualTo(testBean); - try { - converter.setAllowedListPatterns(Collections.singletonList("foo.*")); - fromMessage = converter.fromMessage(message); - assertThat(fromMessage).isEqualTo(testBean); - fail("Expected SecurityException"); - } - catch (SecurityException e) { - - } + converter.setAllowedListPatterns(Collections.singletonList("foo.*")); + assertThatExceptionOfType(SecurityException.class).isThrownBy(() -> converter.fromMessage(message)); } @SuppressWarnings("serial") diff --git a/src/reference/asciidoc/amqp.adoc b/src/reference/asciidoc/amqp.adoc index 3904eada..61cb8990 100644 --- a/src/reference/asciidoc/amqp.adoc +++ b/src/reference/asciidoc/amqp.adoc @@ -4321,7 +4321,7 @@ 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 allowed list is empty, meaning all classes are deserialized. +By default, the allowed list is empty, meaning no classes will be deserialized. You can set a list of patterns, such as `thing1.*`, `thing1.thing2.Cat` or `*.MySafeClass`. @@ -4329,6 +4329,7 @@ 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 `allowedListPatterns` property on these converters. +Alternatively, if you trust all message originators, you can set the environment variable `SPRING_AMQP_DESERIALIZATION_TRUST_ALL` or system property `spring.amqp.deserialization.trust.all` to `true`. ==== [[message-properties-converters]]