Enhance Check (#11419)

This commit is contained in:
Albumen Kevin 2023-02-01 16:33:20 +08:00 committed by GitHub
parent f1b3e55b19
commit c71a05a58c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
27 changed files with 1422 additions and 655 deletions

View File

@ -22,6 +22,8 @@ import org.apache.dubbo.common.convert.ConverterUtil;
import org.apache.dubbo.common.lang.ShutdownHookCallbacks;
import org.apache.dubbo.common.status.reporter.FrameworkStatusReportService;
import org.apache.dubbo.common.threadpool.manager.FrameworkExecutorRepository;
import org.apache.dubbo.common.utils.DefaultSerializeClassChecker;
import org.apache.dubbo.common.utils.SerializeSecurityConfigurator;
import org.apache.dubbo.common.utils.SerializeSecurityManager;
import org.apache.dubbo.rpc.model.ApplicationModel;
import org.apache.dubbo.rpc.model.FrameworkModel;
@ -35,6 +37,7 @@ public class CommonScopeModelInitializer implements ScopeModelInitializer {
beanFactory.registerBean(FrameworkExecutorRepository.class);
beanFactory.registerBean(ConverterUtil.class);
beanFactory.registerBean(SerializeSecurityManager.class);
beanFactory.registerBean(DefaultSerializeClassChecker.class);
}
@Override
@ -49,5 +52,6 @@ public class CommonScopeModelInitializer implements ScopeModelInitializer {
public void initializeModuleModel(ModuleModel moduleModel) {
ScopeBeanFactory beanFactory = moduleModel.getBeanFactory();
beanFactory.registerBean(new ConfigurationCache());
beanFactory.registerBean(SerializeSecurityConfigurator.class);
}
}

View File

@ -16,12 +16,6 @@
*/
package org.apache.dubbo.common.beanutil;
import org.apache.dubbo.common.logger.Logger;
import org.apache.dubbo.common.logger.LoggerFactory;
import org.apache.dubbo.common.utils.LogHelper;
import org.apache.dubbo.common.utils.ReflectUtils;
import org.apache.dubbo.common.utils.SerializeClassChecker;
import java.lang.reflect.Array;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
@ -32,6 +26,12 @@ import java.util.HashMap;
import java.util.IdentityHashMap;
import java.util.Map;
import org.apache.dubbo.common.logger.Logger;
import org.apache.dubbo.common.logger.LoggerFactory;
import org.apache.dubbo.common.utils.DefaultSerializeClassChecker;
import org.apache.dubbo.common.utils.LogHelper;
import org.apache.dubbo.common.utils.ReflectUtils;
public final class JavaBeanSerializeUtil {
private static final Logger logger = LoggerFactory.getLogger(JavaBeanSerializeUtil.class);
@ -465,8 +465,7 @@ public final class JavaBeanSerializeUtil {
if (isReferenceType(name)) {
name = name.substring(1, name.length() - 1);
}
SerializeClassChecker.getInstance().validateClass(name);
return Class.forName(name, false, loader);
return DefaultSerializeClassChecker.getInstance().loadClass(loader, name);
}
private static boolean isArray(String type) {

View File

@ -22,5 +22,9 @@ public interface AllowClassNotifyListener {
SerializeCheckStatus DEFAULT_STATUS = SerializeCheckStatus.WARN;
void notify(SerializeCheckStatus status, Set<String> prefixList);
void notifyPrefix(Set<String> allowedList, Set<String> disAllowedList);
void notifyCheckStatus(SerializeCheckStatus status);
void notifyCheckSerializable(boolean checkSerializable);
}

View File

@ -14,17 +14,14 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.dubbo.common.serialize.hessian2;
package org.apache.dubbo.common.utils;
import java.io.Serializable;
import java.util.Arrays;
import java.util.Set;
import org.apache.dubbo.common.logger.ErrorTypeAwareLogger;
import org.apache.dubbo.common.logger.LoggerFactory;
import org.apache.dubbo.common.utils.AllowClassNotifyListener;
import org.apache.dubbo.common.utils.ConcurrentHashSet;
import org.apache.dubbo.common.utils.SerializeCheckStatus;
import org.apache.dubbo.common.utils.SerializeSecurityManager;
import org.apache.dubbo.rpc.model.FrameworkModel;
import static org.apache.dubbo.common.constants.LoggerCodeConstants.PROTOCOL_UNTRUSTED_SERIALIZE_CLASS;
@ -33,26 +30,44 @@ import static org.apache.dubbo.common.constants.LoggerCodeConstants.PROTOCOL_UNT
* Inspired by Fastjson2
* see com.alibaba.fastjson2.filter.ContextAutoTypeBeforeHandler#apply(java.lang.String, java.lang.Class, long)
*/
public class Hessian2AllowClassManager implements AllowClassNotifyListener {
public class DefaultSerializeClassChecker implements AllowClassNotifyListener {
private static final long MAGIC_HASH_CODE = 0xcbf29ce484222325L;
private static final long MAGIC_PRIME = 0x100000001b3L;
private static final ErrorTypeAwareLogger logger = LoggerFactory.getErrorTypeAwareLogger(Hessian2AllowClassManager.class);
private static final ErrorTypeAwareLogger logger = LoggerFactory.getErrorTypeAwareLogger(DefaultSerializeClassChecker.class);
private volatile SerializeCheckStatus checkStatus = AllowClassNotifyListener.DEFAULT_STATUS;
private static final Set<String> warnedClasses = new ConcurrentHashSet<>(1);
private volatile boolean checkSerializable = true;
private volatile long[] allowPrefixes = new long[0];
public Hessian2AllowClassManager(FrameworkModel frameworkModel) {
private volatile long[] disAllowPrefixes = new long[0];
public DefaultSerializeClassChecker(FrameworkModel frameworkModel) {
SerializeSecurityManager serializeSecurityManager = frameworkModel.getBeanFactory().getOrRegisterBean(SerializeSecurityManager.class);
serializeSecurityManager.registerListener(this);
}
@Override
public void notify(SerializeCheckStatus status, Set<String> prefixList) {
public synchronized void notifyPrefix(Set<String> allowedList, Set<String> disAllowedList) {
this.allowPrefixes = loadPrefix(allowedList);
this.disAllowPrefixes = loadPrefix(disAllowedList);
}
@Override
public synchronized void notifyCheckStatus(SerializeCheckStatus status) {
this.checkStatus = status;
long[] array = new long[prefixList.size()];
}
@Override
public synchronized void notifyCheckSerializable(boolean checkSerializable) {
this.checkSerializable = checkSerializable;
}
private static long[] loadPrefix(Set<String> allowedList) {
long[] array = new long[allowedList.size()];
int index = 0;
for (String name : prefixList) {
for (String name : allowedList) {
if (name == null || name.isEmpty()) {
continue;
}
@ -74,11 +89,33 @@ public class Hessian2AllowClassManager implements AllowClassNotifyListener {
array = Arrays.copyOf(array, index);
}
Arrays.sort(array);
this.allowPrefixes = array;
return array;
}
/**
* Try load class
*
* @param className class name
* @throws IllegalArgumentException if class is blocked
*/
public Class<?> loadClass(ClassLoader classLoader, String className) throws ClassNotFoundException {
if (checkStatus == SerializeCheckStatus.DISABLED) {
Class<?> aClass = loadClass0(classLoader, className);
if (checkSerializable && !aClass.isPrimitive() && !Serializable.class.isAssignableFrom(aClass)) {
String msg = "[Serialization Security] Serialized class " + className + " has not implement Serializable interface. " +
"Current mode is strict check, will disallow to deserialize it by default. ";
if (warnedClasses.add(className)) {
logger.error(PROTOCOL_UNTRUSTED_SERIALIZE_CLASS, "", "", msg);
}
throw new IllegalArgumentException(msg);
}
return aClass;
}
private Class<?> loadClass0(ClassLoader classLoader, String className) throws ClassNotFoundException {
if (checkStatus == SerializeCheckStatus.DISABLE) {
return Class.forName(className, false, classLoader);
}
@ -92,7 +129,7 @@ public class Hessian2AllowClassManager implements AllowClassNotifyListener {
hash *= MAGIC_PRIME;
if (Arrays.binarySearch(allowPrefixes, hash) >= 0) {
return Class.forName(className, false, classLoader);
return ClassUtils.forName(className, classLoader);
}
}
@ -105,16 +142,54 @@ public class Hessian2AllowClassManager implements AllowClassNotifyListener {
}
throw new IllegalArgumentException(msg);
} else {
Class<?> clazz = Class.forName(className, false, classLoader);
if (warnedClasses.add(className)) {
logger.error(PROTOCOL_UNTRUSTED_SERIALIZE_CLASS, "", "",
"[Serialization Security] Serialized class " + clazz.getName() + " is not in allow list. " +
}
hash = MAGIC_HASH_CODE;
for (int i = 0, typeNameLength = className.length(); i < typeNameLength; ++i) {
char ch = className.charAt(i);
if (ch == '$') {
ch = '.';
}
hash ^= ch;
hash *= MAGIC_PRIME;
if (Arrays.binarySearch(disAllowPrefixes, hash) >= 0) {
String msg = "[Serialization Security] Serialized class " + className + " is in disallow list. " +
"Current mode is `WARN`, will disallow to deserialize it by default. " +
"Please add it into security/serialize.allowlist or follow FAQ to configure it.";
throw new IllegalArgumentException(msg);
}
}
hash = MAGIC_HASH_CODE;
for (int i = 0, typeNameLength = className.length(); i < typeNameLength; ++i) {
char ch = Character.toLowerCase(className.charAt(i));
if (ch == '$') {
ch = '.';
}
hash ^= ch;
hash *= MAGIC_PRIME;
if (Arrays.binarySearch(disAllowPrefixes, hash) >= 0) {
String msg = "[Serialization Security] Serialized class " + className + " is in disallow list. " +
"Current mode is `WARN`, will disallow to deserialize it by default. " +
"Please add it into security/serialize.allowlist or follow FAQ to configure it.";
throw new IllegalArgumentException(msg);
}
}
Class<?> clazz = ClassUtils.forName(className, classLoader);
if (warnedClasses.add(className)) {
logger.error(PROTOCOL_UNTRUSTED_SERIALIZE_CLASS, "", "",
"[Serialization Security] Serialized class " + className + " is not in allow list. " +
"Current mode is `WARN`, will allow to deserialize it by default. " +
"Dubbo will set to `STRICT` mode by default in the future. " +
"Please add it into security/serialize.allowlist or follow FAQ to configure it.");
}
return clazz;
}
return clazz;
}
public static DefaultSerializeClassChecker getInstance() {
return FrameworkModel.defaultModel().getBeanFactory().getBean(DefaultSerializeClassChecker.class);
}
}

View File

@ -16,11 +16,6 @@
*/
package org.apache.dubbo.common.utils;
import org.apache.dubbo.common.config.ConfigurationUtils;
import org.apache.dubbo.common.constants.CommonConstants;
import org.apache.dubbo.common.logger.ErrorTypeAwareLogger;
import org.apache.dubbo.common.logger.LoggerFactory;
import java.lang.reflect.Array;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
@ -58,6 +53,11 @@ import java.util.concurrent.ConcurrentSkipListMap;
import java.util.function.Consumer;
import java.util.function.Supplier;
import org.apache.dubbo.common.config.ConfigurationUtils;
import org.apache.dubbo.common.constants.CommonConstants;
import org.apache.dubbo.common.logger.ErrorTypeAwareLogger;
import org.apache.dubbo.common.logger.LoggerFactory;
import static org.apache.dubbo.common.constants.LoggerCodeConstants.COMMON_REFLECTIVE_OPERATION_FAILED;
import static org.apache.dubbo.common.utils.ClassUtils.isAssignableFrom;
@ -410,10 +410,9 @@ public class PojoUtils {
if (pojo instanceof Map<?, ?> && type != null) {
Object className = ((Map<Object, Object>) pojo).get("class");
if (className instanceof String) {
SerializeClassChecker.getInstance().validateClass((String) className);
if (!CLASS_NOT_FOUND_CACHE.containsKey(className)) {
try {
type = ClassUtils.forName((String) className);
type = DefaultSerializeClassChecker.getInstance().loadClass(ClassUtils.getClassLoader(), (String) className);
} catch (ClassNotFoundException e) {
CLASS_NOT_FOUND_CACHE.put((String) className, NOT_FOUND_VALUE);
}

View File

@ -17,7 +17,28 @@
package org.apache.dubbo.common.utils;
public enum SerializeCheckStatus {
DISABLED,
WARN,
STRICT,
/**
* Disable serialize check for all classes
*/
DISABLE(0),
/**
* Only deny danger classes, warn if other classes are not in allow list
*/
WARN(1),
/**
* Only allow classes in allow list, deny if other classes are not in allow list
*/
STRICT(2);
private final int level;
SerializeCheckStatus(int level) {
this.level = level;
}
public int level() {
return level;
}
}

View File

@ -1,177 +0,0 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.dubbo.common.utils;
import org.apache.dubbo.common.beanutil.JavaBeanSerializeUtil;
import org.apache.dubbo.common.constants.CommonConstants;
import org.apache.dubbo.common.logger.ErrorTypeAwareLogger;
import org.apache.dubbo.common.logger.LoggerFactory;
import java.io.IOException;
import java.util.Arrays;
import java.util.Locale;
import java.util.Set;
import java.util.concurrent.atomic.AtomicLong;
import static org.apache.dubbo.common.constants.CommonConstants.CLASS_DESERIALIZE_ALLOWED_LIST;
import static org.apache.dubbo.common.constants.CommonConstants.CLASS_DESERIALIZE_BLOCKED_LIST;
import static org.apache.dubbo.common.constants.CommonConstants.CLASS_DESERIALIZE_BLOCK_ALL;
import static org.apache.dubbo.common.constants.CommonConstants.SERIALIZE_BLOCKED_LIST_FILE_PATH;
import static org.apache.dubbo.common.constants.LoggerCodeConstants.COMMON_IO_EXCEPTION;
import static org.apache.dubbo.common.constants.LoggerCodeConstants.PROTOCOL_UNSAFE_SERIALIZATION;
public class SerializeClassChecker {
private static final ErrorTypeAwareLogger logger = LoggerFactory.getErrorTypeAwareLogger(SerializeClassChecker.class);
private static volatile SerializeClassChecker INSTANCE = null;
private final boolean OPEN_CHECK_CLASS;
private final boolean BLOCK_ALL_CLASS_EXCEPT_ALLOW;
private final Set<String> CLASS_DESERIALIZE_ALLOWED_SET = new ConcurrentHashSet<>();
private final Set<String> CLASS_DESERIALIZE_BLOCKED_SET = new ConcurrentHashSet<>();
private final Object CACHE = new Object();
private final LFUCache<String, Object> CLASS_ALLOW_LFU_CACHE = new LFUCache<>();
private final LFUCache<String, Object> CLASS_BLOCK_LFU_CACHE = new LFUCache<>();
private final AtomicLong counter = new AtomicLong(0);
private SerializeClassChecker() {
String openCheckClass = System.getProperty(CommonConstants.CLASS_DESERIALIZE_OPEN_CHECK, "true");
OPEN_CHECK_CLASS = Boolean.parseBoolean(openCheckClass);
String blockAllClassExceptAllow = System.getProperty(CLASS_DESERIALIZE_BLOCK_ALL, "false");
BLOCK_ALL_CLASS_EXCEPT_ALLOW = Boolean.parseBoolean(blockAllClassExceptAllow);
String[] lines;
try {
ClassLoader classLoader = ClassUtils.getClassLoader(JavaBeanSerializeUtil.class);
if (classLoader != null) {
lines = IOUtils.readLines(classLoader.getResourceAsStream(SERIALIZE_BLOCKED_LIST_FILE_PATH));
} else {
lines = IOUtils.readLines(ClassLoader.getSystemResourceAsStream(SERIALIZE_BLOCKED_LIST_FILE_PATH));
}
for (String line : lines) {
line = line.trim();
if (StringUtils.isEmpty(line) || line.startsWith("#")) {
continue;
}
CLASS_DESERIALIZE_BLOCKED_SET.add(line);
}
} catch (IOException e) {
logger.error(COMMON_IO_EXCEPTION, "", "", "Failed to load blocked class list! Will ignore default blocked list.", e);
}
String allowedClassList = System.getProperty(CLASS_DESERIALIZE_ALLOWED_LIST, "").trim().toLowerCase(Locale.ROOT);
String blockedClassList = System.getProperty(CLASS_DESERIALIZE_BLOCKED_LIST, "").trim().toLowerCase(Locale.ROOT);
if (StringUtils.isNotEmpty(allowedClassList)) {
String[] classStrings = allowedClassList.trim().split(",");
CLASS_DESERIALIZE_ALLOWED_SET.addAll(Arrays.asList(classStrings));
}
if (StringUtils.isNotEmpty(blockedClassList)) {
String[] classStrings = blockedClassList.trim().split(",");
CLASS_DESERIALIZE_BLOCKED_SET.addAll(Arrays.asList(classStrings));
}
}
public static SerializeClassChecker getInstance() {
if (INSTANCE == null) {
synchronized (SerializeClassChecker.class) {
if (INSTANCE == null) {
INSTANCE = new SerializeClassChecker();
}
}
}
return INSTANCE;
}
/**
* For ut only
*/
@Deprecated
protected static void clearInstance() {
INSTANCE = null;
}
/**
* Check if a class is in block list, using prefix match
*
* @param name class name ( all are convert to lower case )
* @throws IllegalArgumentException if class is blocked
*/
public void validateClass(String name) {
validateClass(name, true);
}
public boolean validateClass(String name, boolean failOnError) {
if (!OPEN_CHECK_CLASS) {
return true;
}
name = name.toLowerCase(Locale.ROOT);
if (CACHE == CLASS_ALLOW_LFU_CACHE.get(name)) {
return true;
}
if (CACHE == CLASS_BLOCK_LFU_CACHE.get(name)) {
if (failOnError) {
error(name);
}
return false;
}
for (String allowedPrefix : CLASS_DESERIALIZE_ALLOWED_SET) {
if (name.startsWith(allowedPrefix)) {
CLASS_ALLOW_LFU_CACHE.put(name, CACHE);
return true;
}
}
for (String blockedPrefix : CLASS_DESERIALIZE_BLOCKED_SET) {
if (BLOCK_ALL_CLASS_EXCEPT_ALLOW || name.startsWith(blockedPrefix)) {
CLASS_BLOCK_LFU_CACHE.put(name, CACHE);
if (failOnError) {
error(name);
}
return false;
}
}
CLASS_ALLOW_LFU_CACHE.put(name, CACHE);
return true;
}
private void error(String name) {
String notice = "Trigger the safety barrier! " +
"Catch not allowed serialize class. " +
"Class name: " + name + " . " +
"This means currently maybe being attacking by others." +
"If you are sure this is a mistake, " +
"please add this class name to `" + CLASS_DESERIALIZE_ALLOWED_LIST +
"` as a system environment property.";
if (counter.incrementAndGet() % 1000 == 0 || counter.get() < 100) {
logger.error(PROTOCOL_UNSAFE_SERIALIZATION, "", "", notice);
}
throw new IllegalArgumentException(notice);
}
}

View File

@ -0,0 +1,298 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.dubbo.common.utils;
import java.io.IOException;
import java.lang.reflect.Field;
import java.lang.reflect.GenericArrayType;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.lang.reflect.TypeVariable;
import java.lang.reflect.WildcardType;
import java.net.URL;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;
import org.apache.dubbo.common.constants.CommonConstants;
import org.apache.dubbo.common.logger.ErrorTypeAwareLogger;
import org.apache.dubbo.common.logger.LoggerFactory;
import org.apache.dubbo.config.ApplicationConfig;
import org.apache.dubbo.rpc.model.FrameworkModel;
import org.apache.dubbo.rpc.model.ModuleModel;
import org.apache.dubbo.rpc.model.ScopeClassLoaderListener;
import static org.apache.dubbo.common.constants.CommonConstants.CLASS_DESERIALIZE_ALLOWED_LIST;
import static org.apache.dubbo.common.constants.CommonConstants.CLASS_DESERIALIZE_BLOCKED_LIST;
import static org.apache.dubbo.common.constants.CommonConstants.CLASS_DESERIALIZE_BLOCK_ALL;
import static org.apache.dubbo.common.constants.CommonConstants.SERIALIZE_ALLOW_LIST_FILE_PATH;
import static org.apache.dubbo.common.constants.CommonConstants.SERIALIZE_BLOCKED_LIST_FILE_PATH;
import static org.apache.dubbo.common.constants.LoggerCodeConstants.COMMON_IO_EXCEPTION;
public class SerializeSecurityConfigurator implements ScopeClassLoaderListener<ModuleModel> {
private final SerializeSecurityManager serializeSecurityManager;
private static final ErrorTypeAwareLogger logger = LoggerFactory.getErrorTypeAwareLogger(SerializeSecurityConfigurator.class);
private final ModuleModel moduleModel;
private final boolean autoTrustSerializeClass;
private final int trustSerializeClassLevel;
public SerializeSecurityConfigurator(ModuleModel moduleModel) {
this.moduleModel = moduleModel;
moduleModel.addClassLoaderListener(this);
FrameworkModel frameworkModel = moduleModel.getApplicationModel().getFrameworkModel();
serializeSecurityManager = frameworkModel.getBeanFactory().getBean(SerializeSecurityManager.class);
refreshStatus();
refreshConfig();
onAddClassLoader(moduleModel, Thread.currentThread().getContextClassLoader());
Optional<ApplicationConfig> applicationConfig = moduleModel.getApplicationModel().getApplicationConfigManager().getApplication();
autoTrustSerializeClass = applicationConfig.map(ApplicationConfig::getAutoTrustSerializeClass).orElse(true);
trustSerializeClassLevel = applicationConfig.map(ApplicationConfig::getTrustSerializeClassLevel).orElse(3);
serializeSecurityManager.setCheckSerializable(applicationConfig.map(ApplicationConfig::getCheckSerializable).orElse(true));
}
@Override
public void onAddClassLoader(ModuleModel scopeModel, ClassLoader classLoader) {
refreshClassLoader(classLoader);
}
@Override
public void onRemoveClassLoader(ModuleModel scopeModel, ClassLoader classLoader) {
// ignore
}
private void refreshClassLoader(ClassLoader classLoader) {
loadAllow(classLoader);
loadBlocked(classLoader);
}
private void refreshConfig() {
String allowedClassList = System.getProperty(CLASS_DESERIALIZE_ALLOWED_LIST, "").trim();
String blockedClassList = System.getProperty(CLASS_DESERIALIZE_BLOCKED_LIST, "").trim();
if (StringUtils.isNotEmpty(allowedClassList)) {
String[] classStrings = allowedClassList.trim().split(",");
for (String className : classStrings) {
className = className.trim();
if (StringUtils.isNotEmpty(className)) {
serializeSecurityManager.addToAlwaysAllowed(className);
}
}
}
if (StringUtils.isNotEmpty(blockedClassList)) {
String[] classStrings = blockedClassList.trim().split(",");
for (String className : classStrings) {
className = className.trim();
if (StringUtils.isNotEmpty(className)) {
serializeSecurityManager.addToDisAllowed(className);
}
}
}
}
private void loadAllow(ClassLoader classLoader) {
Set<URL> urls = ClassLoaderResourceLoader.loadResources(SERIALIZE_ALLOW_LIST_FILE_PATH, classLoader);
for (URL u : urls) {
try {
logger.info("Read serialize allow list from " + u);
String[] lines = IOUtils.readLines(u.openStream());
for (String line : lines) {
line = line.trim();
if (StringUtils.isEmpty(line) || line.startsWith("#")) {
continue;
}
serializeSecurityManager.addToAlwaysAllowed(line);
}
} catch (IOException e) {
logger.error(COMMON_IO_EXCEPTION, "", "", "Failed to load allow class list! Will ignore allow lis from " + u, e);
}
}
}
private void loadBlocked(ClassLoader classLoader) {
Set<URL> urls = ClassLoaderResourceLoader.loadResources(SERIALIZE_BLOCKED_LIST_FILE_PATH, classLoader);
for (URL u : urls) {
try {
logger.info("Read serialize blocked list from " + u);
String[] lines = IOUtils.readLines(u.openStream());
for (String line : lines) {
line = line.trim();
if (StringUtils.isEmpty(line) || line.startsWith("#")) {
continue;
}
serializeSecurityManager.addToDisAllowed(line);
}
} catch (IOException e) {
logger.error(COMMON_IO_EXCEPTION, "", "", "Failed to load blocked class list! Will ignore blocked lis from " + u, e);
}
}
}
private void refreshStatus() {
Optional<ApplicationConfig> application = moduleModel.getApplicationModel().getApplicationConfigManager().getApplication();
String statusString = application.map(ApplicationConfig::getSerializeCheckStatus).orElse(null);
SerializeCheckStatus checkStatus = null;
if (StringUtils.isEmpty(statusString)) {
String openCheckClass = System.getProperty(CommonConstants.CLASS_DESERIALIZE_OPEN_CHECK, "true");
if (!Boolean.parseBoolean(openCheckClass)) {
checkStatus = SerializeCheckStatus.DISABLE;
}
String blockAllClassExceptAllow = System.getProperty(CLASS_DESERIALIZE_BLOCK_ALL, "false");
if (Boolean.parseBoolean(blockAllClassExceptAllow)) {
checkStatus = SerializeCheckStatus.STRICT;
}
} else {
checkStatus = SerializeCheckStatus.valueOf(statusString);
}
if (checkStatus != null) {
serializeSecurityManager.setCheckStatus(checkStatus);
}
}
public synchronized void registerInterface(Class<?> clazz) {
if (!autoTrustSerializeClass) {
return;
}
Set<Class<?>> markedClass = new HashSet<>();
markedClass.add(clazz);
addToAllow(clazz.getName());
Method[] methodsToExport = clazz.getMethods();
for (Method method : methodsToExport) {
Class<?>[] parameterTypes = method.getParameterTypes();
for (Class<?> parameterType : parameterTypes) {
checkClass(markedClass, parameterType);
}
Type[] genericParameterTypes = method.getGenericParameterTypes();
for (Type genericParameterType : genericParameterTypes) {
checkType(markedClass, genericParameterType);
}
Class<?> returnType = method.getReturnType();
checkClass(markedClass, returnType);
Type genericReturnType = method.getGenericReturnType();
checkType(markedClass, genericReturnType);
Class<?>[] exceptionTypes = method.getExceptionTypes();
for (Class<?> exceptionType : exceptionTypes) {
checkClass(markedClass, exceptionType);
}
Type[] genericExceptionTypes = method.getGenericExceptionTypes();
for (Type genericExceptionType : genericExceptionTypes) {
checkType(markedClass, genericExceptionType);
}
}
}
private void checkType(Set<Class<?>> markedClass, Type type) {
if (type instanceof Class) {
checkClass(markedClass, (Class<?>) type);
} else if (type instanceof ParameterizedType) {
ParameterizedType parameterizedType = (ParameterizedType) type;
checkClass(markedClass, (Class<?>) parameterizedType.getRawType());
for (Type actualTypeArgument : parameterizedType.getActualTypeArguments()) {
checkType(markedClass, actualTypeArgument);
}
} else if (type instanceof GenericArrayType) {
GenericArrayType genericArrayType = (GenericArrayType) type;
checkType(markedClass, genericArrayType.getGenericComponentType());
} else if (type instanceof TypeVariable) {
TypeVariable typeVariable = (TypeVariable) type;
for (Type bound : typeVariable.getBounds()) {
checkType(markedClass, bound);
}
} else if (type instanceof WildcardType) {
WildcardType wildcardType = (WildcardType) type;
for (Type bound : wildcardType.getUpperBounds()) {
checkType(markedClass, bound);
}
for (Type bound : wildcardType.getLowerBounds()) {
checkType(markedClass, bound);
}
}
}
private void checkClass(Set<Class<?>> markedClass, Class<?> clazz) {
if (markedClass.contains(clazz)) {
return;
}
markedClass.add(clazz);
addToAllow(clazz.getName());
Class<?>[] interfaces = clazz.getInterfaces();
for (Class<?> interfaceClass : interfaces) {
checkClass(markedClass, interfaceClass);
}
Class<?> superclass = clazz.getSuperclass();
if (superclass != null) {
checkClass(markedClass, superclass);
}
Field[] fields = clazz.getDeclaredFields();
for (Field field : fields) {
if (Modifier.isTransient(field.getModifiers())) {
continue;
}
Class<?> fieldClass = field.getType();
checkClass(markedClass, fieldClass);
checkType(markedClass, field.getGenericType());
}
}
private void addToAllow(String className) {
// ignore jdk
if (className.startsWith("java.") || className.startsWith("javax.") || className.startsWith("com.sun.") ||
className.startsWith("sun.") || className.startsWith("jdk.")) {
serializeSecurityManager.addToAllowed(className);
return;
}
// add group package
String[] subs = className.split("\\.");
if (subs.length > trustSerializeClassLevel) {
serializeSecurityManager.addToAllowed(Arrays.stream(subs)
.limit(trustSerializeClassLevel)
.collect(Collectors.joining(".")));
} else {
serializeSecurityManager.addToAllowed(className);
}
}
}

View File

@ -16,214 +16,131 @@
*/
package org.apache.dubbo.common.utils;
import java.io.IOException;
import java.lang.reflect.Field;
import java.lang.reflect.GenericArrayType;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.lang.reflect.TypeVariable;
import java.lang.reflect.WildcardType;
import java.net.URL;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.Objects;
import java.util.Set;
import java.util.stream.Collectors;
import org.apache.dubbo.common.logger.ErrorTypeAwareLogger;
import org.apache.dubbo.common.logger.LoggerFactory;
import org.apache.dubbo.rpc.model.FrameworkModel;
import static org.apache.dubbo.common.constants.CommonConstants.SERIALIZE_ALLOW_LIST_FILE_PATH;
import static org.apache.dubbo.common.constants.CommonConstants.SERIALIZE_CHECK_STATUS_KEY;
import static org.apache.dubbo.common.constants.LoggerCodeConstants.COMMON_IO_EXCEPTION;
import static org.apache.dubbo.common.constants.LoggerCodeConstants.INTERNAL_INTERRUPTED;
public class SerializeSecurityManager {
private final Set<String> allowedPrefix = new ConcurrentHashSet<>();
private static final ErrorTypeAwareLogger logger = LoggerFactory.getErrorTypeAwareLogger(SerializeSecurityManager.class);
private final SerializeClassChecker checker = SerializeClassChecker.getInstance();
private final Set<String> allowedPrefix = new ConcurrentHashSet<>();
private final Set<String> alwaysAllowedPrefix = new ConcurrentHashSet<>();
private final Set<String> disAllowedPrefix = new ConcurrentHashSet<>();
private final Set<AllowClassNotifyListener> listeners = new ConcurrentHashSet<>();
private volatile SerializeCheckStatus checkStatus = AllowClassNotifyListener.DEFAULT_STATUS;
private volatile SerializeCheckStatus checkStatus = null;
public SerializeSecurityManager(FrameworkModel frameworkModel) {
try {
Set<ClassLoader> classLoaders = frameworkModel.getClassLoaders();
List<URL> urls = ClassLoaderResourceLoader.loadResources(SERIALIZE_ALLOW_LIST_FILE_PATH, classLoaders)
.values()
.stream()
.flatMap(Set::stream)
.collect(Collectors.toList());
for (URL u : urls) {
try {
logger.info("Read serialize allow list from " + u);
String[] lines = IOUtils.readLines(u.openStream());
for (String line : lines) {
line = line.trim();
if (StringUtils.isEmpty(line) || line.startsWith("#")) {
continue;
}
allowedPrefix.add(line);
}
} catch (IOException e) {
logger.error(COMMON_IO_EXCEPTION, "", "", "Failed to load allow class list! Will ignore allow lis from " + u, e);
}
}
private volatile Boolean checkSerializable = null;
this.checkStatus = SerializeCheckStatus.valueOf(System.getProperty(SERIALIZE_CHECK_STATUS_KEY, AllowClassNotifyListener.DEFAULT_STATUS.name()));
logger.info("Serialize check level: " + checkStatus.name());
} catch (InterruptedException e) {
logger.error(INTERNAL_INTERRUPTED, "", "", "Failed to load allow class list! Will ignore allow list from configuration.", e);
Thread.currentThread().interrupt();
}
}
public synchronized void registerInterface(Class<?> clazz) {
Set<Class<?>> markedClass = new HashSet<>();
markedClass.add(clazz);
addToAllow(clazz.getName());
Method[] methodsToExport = clazz.getMethods();
for (Method method : methodsToExport) {
Class<?>[] parameterTypes = method.getParameterTypes();
for (Class<?> parameterType : parameterTypes) {
checkClass(markedClass, parameterType);
}
Type[] genericParameterTypes = method.getGenericParameterTypes();
for (Type genericParameterType : genericParameterTypes) {
checkType(markedClass, genericParameterType);
}
Class<?> returnType = method.getReturnType();
checkClass(markedClass, returnType);
Type genericReturnType = method.getGenericReturnType();
checkType(markedClass, genericReturnType);
Class<?>[] exceptionTypes = method.getExceptionTypes();
for (Class<?> exceptionType : exceptionTypes) {
checkClass(markedClass, exceptionType);
}
Type[] genericExceptionTypes = method.getGenericExceptionTypes();
for (Type genericExceptionType : genericExceptionTypes) {
checkType(markedClass, genericExceptionType);
}
}
}
private void checkType(Set<Class<?>> markedClass, Type type) {
if (type instanceof Class) {
checkClass(markedClass, (Class<?>) type);
} else if (type instanceof ParameterizedType) {
ParameterizedType parameterizedType = (ParameterizedType) type;
checkClass(markedClass, (Class<?>) parameterizedType.getRawType());
for (Type actualTypeArgument : parameterizedType.getActualTypeArguments()) {
checkType(markedClass, actualTypeArgument);
}
} else if (type instanceof GenericArrayType) {
GenericArrayType genericArrayType = (GenericArrayType) type;
checkType(markedClass, genericArrayType.getGenericComponentType());
} else if (type instanceof TypeVariable) {
TypeVariable typeVariable = (TypeVariable) type;
for (Type bound : typeVariable.getBounds()) {
checkType(markedClass, bound);
}
} else if (type instanceof WildcardType) {
WildcardType wildcardType = (WildcardType) type;
for (Type bound : wildcardType.getUpperBounds()) {
checkType(markedClass, bound);
}
for (Type bound : wildcardType.getLowerBounds()) {
checkType(markedClass, bound);
}
}
}
private void checkClass(Set<Class<?>> markedClass, Class<?> clazz) {
if (markedClass.contains(clazz)) {
return;
}
markedClass.add(clazz);
addToAllow(clazz.getName());
Class<?>[] interfaces = clazz.getInterfaces();
for (Class<?> interfaceClass : interfaces) {
checkClass(markedClass, interfaceClass);
}
Class<?> superclass = clazz.getSuperclass();
if (superclass != null) {
checkClass(markedClass, superclass);
}
Field[] fields = clazz.getDeclaredFields();
for (Field field : fields) {
if (Modifier.isTransient(field.getModifiers())) {
continue;
}
Class<?> fieldClass = field.getType();
checkClass(markedClass, fieldClass);
checkType(markedClass, field.getGenericType());
}
}
protected void addToAllow(String className) {
if (!checker.validateClass(className, false)) {
return;
}
boolean modified;
// ignore jdk
if (className.startsWith("java.") || className.startsWith("javax.") || className.startsWith("com.sun.") ||
className.startsWith("sun.") || className.startsWith("jdk.")) {
modified = allowedPrefix.add(className);
if (modified) {
notifyListeners();
}
return;
}
// add group package
String[] subs = className.split("\\.");
if (subs.length > 3) {
modified = allowedPrefix.add(subs[0] + "." + subs[1] + "." + subs[2]);
} else {
modified = allowedPrefix.add(className);
}
public void addToAlwaysAllowed(String className) {
boolean modified = alwaysAllowedPrefix.add(className);
if (modified) {
notifyListeners();
notifyPrefix();
}
}
public void addToAllowed(String className) {
if (disAllowedPrefix.stream().anyMatch(className::startsWith)) {
return;
}
boolean modified = allowedPrefix.add(className);
if (modified) {
notifyPrefix();
}
}
public void addToDisAllowed(String className) {
boolean modified = disAllowedPrefix.add(className);
modified = allowedPrefix.removeIf(allow -> allow.startsWith(className)) || modified;
if (modified) {
notifyPrefix();
}
String lowerCase = className.toLowerCase(Locale.ROOT);
if (!Objects.equals(lowerCase, className)) {
addToDisAllowed(lowerCase);
}
}
public void setCheckStatus(SerializeCheckStatus checkStatus) {
if (this.checkStatus == null) {
this.checkStatus = checkStatus;
logger.info("Serialize check level: " + checkStatus.name());
notifyCheckStatus();
return;
}
// If has been set to WARN, ignore STRICT
if (this.checkStatus.level() < checkStatus.level()) {
return;
}
this.checkStatus = checkStatus;
logger.info("Serialize check level: " + checkStatus.name());
notifyCheckStatus();
}
public void setCheckSerializable(boolean checkSerializable) {
if (this.checkSerializable == null || (Boolean.TRUE.equals(this.checkSerializable) && !checkSerializable)) {
this.checkSerializable = checkSerializable;
logger.info("Serialize check serializable: " + checkSerializable);
notifyCheckSerializable();
}
}
public void registerListener(AllowClassNotifyListener listener) {
listeners.add(listener);
listener.notify(checkStatus, getAllowedPrefix());
listener.notifyPrefix(getAllowedPrefix(), getDisAllowedPrefix());
listener.notifyCheckSerializable(isCheckSerializable());
listener.notifyCheckStatus(getCheckStatus());
}
private void notifyListeners() {
private void notifyPrefix() {
for (AllowClassNotifyListener listener : listeners) {
listener.notify(checkStatus, getAllowedPrefix());
listener.notifyPrefix(getAllowedPrefix(), getDisAllowedPrefix());
}
}
private void notifyCheckStatus() {
for (AllowClassNotifyListener listener : listeners) {
listener.notifyCheckStatus(getCheckStatus());
}
}
private void notifyCheckSerializable() {
for (AllowClassNotifyListener listener : listeners) {
listener.notifyCheckSerializable(isCheckSerializable());
}
}
protected SerializeCheckStatus getCheckStatus() {
return checkStatus == null ? AllowClassNotifyListener.DEFAULT_STATUS : checkStatus;
}
protected Set<String> getAllowedPrefix() {
Set<String> set = new ConcurrentHashSet<>();
set.addAll(allowedPrefix);
set.addAll(alwaysAllowedPrefix);
return set;
}
protected Set<String> getDisAllowedPrefix() {
Set<String> set = new ConcurrentHashSet<>();
set.addAll(disAllowedPrefix);
return set;
}
protected boolean isCheckSerializable() {
return checkSerializable == null || checkSerializable;
}
}

View File

@ -216,6 +216,14 @@ public class ApplicationConfig extends AbstractConfig {
private Boolean enableEmptyProtection;
private String serializeCheckStatus;
private Boolean autoTrustSerializeClass;
private Integer trustSerializeClassLevel;
private Boolean checkSerializable;
public ApplicationConfig() {
}
@ -613,6 +621,39 @@ public class ApplicationConfig extends AbstractConfig {
this.startupProbe = startupProbe;
}
public String getSerializeCheckStatus() {
return serializeCheckStatus;
}
public void setSerializeCheckStatus(String serializeCheckStatus) {
this.serializeCheckStatus = serializeCheckStatus;
}
public Boolean getAutoTrustSerializeClass() {
return autoTrustSerializeClass;
}
public void setAutoTrustSerializeClass(Boolean autoTrustSerializeClass) {
this.autoTrustSerializeClass = autoTrustSerializeClass;
}
public Integer getTrustSerializeClassLevel() {
return trustSerializeClassLevel;
}
public void setTrustSerializeClassLevel(Integer trustSerializeClassLevel) {
this.trustSerializeClassLevel = trustSerializeClassLevel;
}
public Boolean getCheckSerializable() {
return checkSerializable;
}
public void setCheckSerializable(Boolean checkSerializable) {
this.checkSerializable = checkSerializable;
}
@Override
public void refresh() {
super.refresh();

View File

@ -0,0 +1,20 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.service.deep1.deep2.deep3;
public interface DemoService3 {
}

View File

@ -16,15 +16,16 @@
*/
package org.apache.dubbo.common.beanutil;
import org.apache.dubbo.rpc.model.person.FullAddress;
import org.apache.dubbo.rpc.model.person.PersonStatus;
import org.apache.dubbo.rpc.model.person.Phone;
import java.io.Serializable;
import java.util.Collection;
import java.util.Date;
import java.util.Map;
public class Bean {
import org.apache.dubbo.rpc.model.person.FullAddress;
import org.apache.dubbo.rpc.model.person.PersonStatus;
import org.apache.dubbo.rpc.model.person.Phone;
public class Bean implements Serializable {
private Class<?> type;

View File

@ -16,9 +16,10 @@
*/
package org.apache.dubbo.common.model;
import java.io.Serializable;
import java.util.Arrays;
public class Person {
public class Person implements Serializable {
byte oneByte = 123;
private String name = "name1";
private int age = 11;

View File

@ -17,12 +17,13 @@
package org.apache.dubbo.common.model;
import java.io.Serializable;
import java.util.Objects;
/**
* this class has no nullary constructor and some field is primitive
*/
public class User {
public class User implements Serializable {
private int age;
private String name;

View File

@ -0,0 +1,123 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.dubbo.common.utils;
import java.net.Socket;
import java.util.LinkedList;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import org.apache.dubbo.common.constants.CommonConstants;
import org.apache.dubbo.rpc.model.FrameworkModel;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
class DefaultSerializeClassCheckerTest {
@BeforeEach
void setUp() {
FrameworkModel.destroyAll();
}
@AfterEach
void tearDown() {
FrameworkModel.destroyAll();
}
@Test
void testCommon() throws ClassNotFoundException {
DefaultSerializeClassChecker defaultSerializeClassChecker = DefaultSerializeClassChecker.getInstance();
for (int i = 0; i < 10; i++) {
defaultSerializeClassChecker.loadClass(Thread.currentThread().getContextClassLoader(), ReentrantReadWriteLock.ReadLock.class.getName());
defaultSerializeClassChecker.loadClass(Thread.currentThread().getContextClassLoader(), LinkedList.class.getName());
defaultSerializeClassChecker.loadClass(Thread.currentThread().getContextClassLoader(), Integer.class.getName());
defaultSerializeClassChecker.loadClass(Thread.currentThread().getContextClassLoader(), int.class.getName());
}
Assertions.assertThrows(IllegalArgumentException.class, () -> {
defaultSerializeClassChecker.loadClass(Thread.currentThread().getContextClassLoader(), Socket.class.getName());
});
}
@Test
void testAddAllow() throws ClassNotFoundException {
System.setProperty(CommonConstants.CLASS_DESERIALIZE_ALLOWED_LIST, ReentrantReadWriteLock.WriteLock.class.getName() + "," + ReentrantReadWriteLock.ReadLock.class.getName());
DefaultSerializeClassChecker defaultSerializeClassChecker = DefaultSerializeClassChecker.getInstance();
for (int i = 0; i < 10; i++) {
defaultSerializeClassChecker.loadClass(Thread.currentThread().getContextClassLoader(), ReentrantReadWriteLock.WriteLock.class.getName());
defaultSerializeClassChecker.loadClass(Thread.currentThread().getContextClassLoader(), ReentrantReadWriteLock.ReadLock.class.getName());
}
System.clearProperty(CommonConstants.CLASS_DESERIALIZE_ALLOWED_LIST);
}
@Test
void testAddBlock() {
System.setProperty(CommonConstants.CLASS_DESERIALIZE_BLOCKED_LIST, Runtime.class.getName() + "," + Thread.class.getName());
DefaultSerializeClassChecker defaultSerializeClassChecker = DefaultSerializeClassChecker.getInstance();
for (int i = 0; i < 10; i++) {
Assertions.assertThrows(IllegalArgumentException.class, () -> {
defaultSerializeClassChecker.loadClass(Thread.currentThread().getContextClassLoader(), Runtime.class.getName());
});
Assertions.assertThrows(IllegalArgumentException.class, () -> {
defaultSerializeClassChecker.loadClass(Thread.currentThread().getContextClassLoader(), Thread.class.getName());
});
}
System.clearProperty(CommonConstants.CLASS_DESERIALIZE_BLOCKED_LIST);
}
@Test
void testBlockAll() throws ClassNotFoundException {
System.setProperty(CommonConstants.CLASS_DESERIALIZE_BLOCK_ALL, "true");
System.setProperty(CommonConstants.CLASS_DESERIALIZE_ALLOWED_LIST, ReentrantReadWriteLock.WriteLock.class.getName());
DefaultSerializeClassChecker defaultSerializeClassChecker = DefaultSerializeClassChecker.getInstance();
for (int i = 0; i < 10; i++) {
defaultSerializeClassChecker.loadClass(Thread.currentThread().getContextClassLoader(), ReentrantReadWriteLock.WriteLock.class.getName());
Assertions.assertThrows(IllegalArgumentException.class, () -> {
defaultSerializeClassChecker.loadClass(Thread.currentThread().getContextClassLoader(), ReentrantReadWriteLock.ReadLock.class.getName());
});
}
System.clearProperty(CommonConstants.CLASS_DESERIALIZE_BLOCK_ALL);
System.clearProperty(CommonConstants.CLASS_DESERIALIZE_ALLOWED_LIST);
}
@Test
void testStatus() throws ClassNotFoundException {
FrameworkModel frameworkModel = FrameworkModel.defaultModel();
SerializeSecurityManager ssm = frameworkModel.getBeanFactory().getBean(SerializeSecurityManager.class);
ssm.setCheckStatus(SerializeCheckStatus.STRICT);
DefaultSerializeClassChecker defaultSerializeClassChecker = DefaultSerializeClassChecker.getInstance();
Assertions.assertEquals(Integer.class, defaultSerializeClassChecker.loadClass(Thread.currentThread().getContextClassLoader(), Integer.class.getName()));
Assertions.assertThrows(IllegalArgumentException.class, () -> {
defaultSerializeClassChecker.loadClass(Thread.currentThread().getContextClassLoader(), ReentrantReadWriteLock.class.getName());
});
ssm.setCheckStatus(SerializeCheckStatus.WARN);
Assertions.assertEquals(ReentrantReadWriteLock.class, defaultSerializeClassChecker.loadClass(Thread.currentThread().getContextClassLoader(), ReentrantReadWriteLock.class.getName()));
ssm.setCheckStatus(SerializeCheckStatus.DISABLE);
Assertions.assertEquals(ReentrantReadWriteLock.class, defaultSerializeClassChecker.loadClass(Thread.currentThread().getContextClassLoader(), ReentrantReadWriteLock.class.getName()));
}
}

View File

@ -16,20 +16,7 @@
*/
package org.apache.dubbo.common.utils;
import org.apache.dubbo.common.model.Person;
import org.apache.dubbo.common.model.SerializablePerson;
import org.apache.dubbo.common.model.User;
import org.apache.dubbo.common.model.person.BigPerson;
import org.apache.dubbo.common.model.person.FullAddress;
import org.apache.dubbo.common.model.person.PersonInfo;
import org.apache.dubbo.common.model.person.PersonMap;
import org.apache.dubbo.common.model.person.PersonStatus;
import org.apache.dubbo.common.model.person.Phone;
import com.alibaba.fastjson.JSONObject;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import java.io.Serializable;
import java.lang.reflect.Method;
import java.lang.reflect.Type;
import java.text.SimpleDateFormat;
@ -47,6 +34,20 @@ import java.util.List;
import java.util.Map;
import java.util.UUID;
import org.apache.dubbo.common.model.Person;
import org.apache.dubbo.common.model.SerializablePerson;
import org.apache.dubbo.common.model.User;
import org.apache.dubbo.common.model.person.BigPerson;
import org.apache.dubbo.common.model.person.FullAddress;
import org.apache.dubbo.common.model.person.PersonInfo;
import org.apache.dubbo.common.model.person.PersonMap;
import org.apache.dubbo.common.model.person.PersonStatus;
import org.apache.dubbo.common.model.person.Phone;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import com.alibaba.fastjson.JSONObject;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.equalTo;
import static org.junit.jupiter.api.Assertions.assertArrayEquals;
@ -793,7 +794,7 @@ class PojoUtilsTest {
SUNDAY, MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY
}
public static class BasicTestData {
public static class BasicTestData implements Serializable {
public boolean a;
public char b;
@ -865,7 +866,7 @@ class PojoUtilsTest {
}
public static class Parent {
public static class Parent implements Serializable {
public String gender;
public String email;
String name;
@ -910,7 +911,7 @@ class PojoUtilsTest {
}
}
public static class Child {
public static class Child implements Serializable {
public String gender;
public int age;
String toy;
@ -950,7 +951,7 @@ class PojoUtilsTest {
}
}
public static class TestData {
public static class TestData implements Serializable {
private Map<String, Child> children = new HashMap<String, Child>();
private List<Child> list = new ArrayList<Child>();
@ -979,7 +980,7 @@ class PojoUtilsTest {
}
}
public static class InnerPojo<T> {
public static class InnerPojo<T> implements Serializable {
private List<T> list;
public List<T> getList() {
@ -991,7 +992,7 @@ class PojoUtilsTest {
}
}
public static class ListResult<T> {
public static class ListResult<T> implements Serializable {
List<T> result;
public List<T> getResult() {

View File

@ -1,111 +0,0 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.dubbo.common.utils;
import org.apache.dubbo.common.constants.CommonConstants;
import javassist.compiler.Javac;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import java.net.Socket;
import java.util.LinkedList;
import java.util.List;
import java.util.Locale;
class SerializeClassCheckerTest {
@BeforeEach
public void setUp() {
SerializeClassChecker.clearInstance();
}
@AfterAll
public static void tearDown() {
SerializeClassChecker.clearInstance();
}
@Test
void testCommon() {
SerializeClassChecker serializeClassChecker = SerializeClassChecker.getInstance();
for (int i = 0; i < 10; i++) {
serializeClassChecker.validateClass(List.class.getName());
serializeClassChecker.validateClass(LinkedList.class.getName());
serializeClassChecker.validateClass(Integer.class.getName());
serializeClassChecker.validateClass(int.class.getName());
serializeClassChecker.validateClass(List.class.getName().toUpperCase(Locale.ROOT));
serializeClassChecker.validateClass(LinkedList.class.getName().toUpperCase(Locale.ROOT));
serializeClassChecker.validateClass(Integer.class.getName().toUpperCase(Locale.ROOT));
serializeClassChecker.validateClass(int.class.getName().toUpperCase(Locale.ROOT));
}
Assertions.assertThrows(IllegalArgumentException.class, ()-> {
serializeClassChecker.validateClass(Socket.class.getName());
});
}
@Test
void testAddAllow() {
System.setProperty(CommonConstants.CLASS_DESERIALIZE_ALLOWED_LIST, Socket.class.getName() + "," + Javac.class.getName());
SerializeClassChecker serializeClassChecker = SerializeClassChecker.getInstance();
for (int i = 0; i < 10; i++) {
serializeClassChecker.validateClass(Socket.class.getName());
serializeClassChecker.validateClass(Javac.class.getName());
}
System.clearProperty(CommonConstants.CLASS_DESERIALIZE_ALLOWED_LIST);
}
@Test
void testAddBlock() {
System.setProperty(CommonConstants.CLASS_DESERIALIZE_BLOCKED_LIST, LinkedList.class.getName() + "," + Integer.class.getName());
SerializeClassChecker serializeClassChecker = SerializeClassChecker.getInstance();
for (int i = 0; i < 10; i++) {
Assertions.assertThrows(IllegalArgumentException.class, ()-> {
serializeClassChecker.validateClass(LinkedList.class.getName());
});
Assertions.assertThrows(IllegalArgumentException.class, ()-> {
serializeClassChecker.validateClass(Integer.class.getName());
});
}
System.clearProperty(CommonConstants.CLASS_DESERIALIZE_BLOCKED_LIST);
}
@Test
void testBlockAll() {
System.setProperty(CommonConstants.CLASS_DESERIALIZE_BLOCK_ALL, "true");
System.setProperty(CommonConstants.CLASS_DESERIALIZE_ALLOWED_LIST, LinkedList.class.getName());
SerializeClassChecker serializeClassChecker = SerializeClassChecker.getInstance();
for (int i = 0; i < 10; i++) {
serializeClassChecker.validateClass(LinkedList.class.getName());
Assertions.assertThrows(IllegalArgumentException.class, ()-> {
serializeClassChecker.validateClass(Integer.class.getName());
});
}
System.clearProperty(CommonConstants.CLASS_DESERIALIZE_BLOCK_ALL);
System.clearProperty(CommonConstants.CLASS_DESERIALIZE_ALLOWED_LIST);
}
}

View File

@ -0,0 +1,390 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.dubbo.common.utils;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.Vector;
import org.apache.dubbo.common.constants.CommonConstants;
import org.apache.dubbo.config.ApplicationConfig;
import org.apache.dubbo.rpc.model.ApplicationModel;
import org.apache.dubbo.rpc.model.FrameworkModel;
import org.apache.dubbo.rpc.model.ModuleModel;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import com.service.DemoService1;
import com.service.DemoService2;
import com.service.deep1.deep2.deep3.DemoService3;
import static org.apache.dubbo.common.constants.CommonConstants.CLASS_DESERIALIZE_ALLOWED_LIST;
import static org.apache.dubbo.common.constants.CommonConstants.CLASS_DESERIALIZE_BLOCKED_LIST;
class SerializeSecurityConfiguratorTest {
@Test
void test() {
FrameworkModel frameworkModel = new FrameworkModel();
ApplicationModel applicationModel = frameworkModel.newApplication();
ModuleModel moduleModel = applicationModel.newModule();
SerializeSecurityManager ssm = frameworkModel.getBeanFactory().getBean(SerializeSecurityManager.class);
SerializeSecurityConfigurator serializeSecurityConfigurator = new SerializeSecurityConfigurator(moduleModel);
serializeSecurityConfigurator.onAddClassLoader(moduleModel, Thread.currentThread().getContextClassLoader());
Assertions.assertTrue(ssm.getAllowedPrefix().contains("java.util.HashMap"));
Assertions.assertTrue(ssm.getAllowedPrefix().contains("com.example.DemoInterface"));
Assertions.assertTrue(ssm.getAllowedPrefix().contains("com.sun.Interface1"));
Assertions.assertTrue(ssm.getDisAllowedPrefix().contains("com.exampletest.DemoInterface"));
Assertions.assertFalse(ssm.getAllowedPrefix().contains("com.sun.Interface2"));
Assertions.assertEquals(AllowClassNotifyListener.DEFAULT_STATUS, ssm.getCheckStatus());
frameworkModel.destroy();
}
@Test
void testStatus1() {
FrameworkModel frameworkModel = new FrameworkModel();
ApplicationModel applicationModel = frameworkModel.newApplication();
ModuleModel moduleModel = applicationModel.newModule();
ApplicationConfig applicationConfig = new ApplicationConfig("Test");
applicationConfig.setSerializeCheckStatus(SerializeCheckStatus.DISABLE.name());
applicationModel.getApplicationConfigManager().setApplication(applicationConfig);
SerializeSecurityManager ssm = frameworkModel.getBeanFactory().getBean(SerializeSecurityManager.class);
SerializeSecurityConfigurator serializeSecurityConfigurator = new SerializeSecurityConfigurator(moduleModel);
serializeSecurityConfigurator.onAddClassLoader(moduleModel, Thread.currentThread().getContextClassLoader());
Assertions.assertEquals(SerializeCheckStatus.DISABLE, ssm.getCheckStatus());
frameworkModel.destroy();
}
@Test
void testStatus2() {
FrameworkModel frameworkModel = new FrameworkModel();
ApplicationModel applicationModel = frameworkModel.newApplication();
ModuleModel moduleModel = applicationModel.newModule();
ApplicationConfig applicationConfig = new ApplicationConfig("Test");
applicationConfig.setSerializeCheckStatus(SerializeCheckStatus.WARN.name());
applicationModel.getApplicationConfigManager().setApplication(applicationConfig);
SerializeSecurityManager ssm = frameworkModel.getBeanFactory().getBean(SerializeSecurityManager.class);
SerializeSecurityConfigurator serializeSecurityConfigurator = new SerializeSecurityConfigurator(moduleModel);
serializeSecurityConfigurator.onAddClassLoader(moduleModel, Thread.currentThread().getContextClassLoader());
Assertions.assertEquals(SerializeCheckStatus.WARN, ssm.getCheckStatus());
frameworkModel.destroy();
}
@Test
void testStatus3() {
FrameworkModel frameworkModel = new FrameworkModel();
ApplicationModel applicationModel = frameworkModel.newApplication();
ModuleModel moduleModel = applicationModel.newModule();
ApplicationConfig applicationConfig = new ApplicationConfig("Test");
applicationConfig.setSerializeCheckStatus(SerializeCheckStatus.STRICT.name());
applicationModel.getApplicationConfigManager().setApplication(applicationConfig);
SerializeSecurityManager ssm = frameworkModel.getBeanFactory().getBean(SerializeSecurityManager.class);
SerializeSecurityConfigurator serializeSecurityConfigurator = new SerializeSecurityConfigurator(moduleModel);
serializeSecurityConfigurator.onAddClassLoader(moduleModel, Thread.currentThread().getContextClassLoader());
Assertions.assertEquals(SerializeCheckStatus.STRICT, ssm.getCheckStatus());
frameworkModel.destroy();
}
@Test
void testStatus4() {
FrameworkModel frameworkModel = new FrameworkModel();
ApplicationModel applicationModel = frameworkModel.newApplication();
ModuleModel moduleModel = applicationModel.newModule();
System.setProperty(CommonConstants.CLASS_DESERIALIZE_OPEN_CHECK, "false");
SerializeSecurityManager ssm = frameworkModel.getBeanFactory().getBean(SerializeSecurityManager.class);
SerializeSecurityConfigurator serializeSecurityConfigurator = new SerializeSecurityConfigurator(moduleModel);
serializeSecurityConfigurator.onAddClassLoader(moduleModel, Thread.currentThread().getContextClassLoader());
Assertions.assertEquals(SerializeCheckStatus.DISABLE, ssm.getCheckStatus());
System.clearProperty(CommonConstants.CLASS_DESERIALIZE_OPEN_CHECK);
frameworkModel.destroy();
}
@Test
void testStatus5() {
FrameworkModel frameworkModel = new FrameworkModel();
ApplicationModel applicationModel = frameworkModel.newApplication();
ModuleModel moduleModel = applicationModel.newModule();
System.setProperty(CommonConstants.CLASS_DESERIALIZE_BLOCK_ALL, "true");
SerializeSecurityManager ssm = frameworkModel.getBeanFactory().getBean(SerializeSecurityManager.class);
SerializeSecurityConfigurator serializeSecurityConfigurator = new SerializeSecurityConfigurator(moduleModel);
serializeSecurityConfigurator.onAddClassLoader(moduleModel, Thread.currentThread().getContextClassLoader());
Assertions.assertEquals(SerializeCheckStatus.STRICT, ssm.getCheckStatus());
System.clearProperty(CommonConstants.CLASS_DESERIALIZE_BLOCK_ALL);
frameworkModel.destroy();
}
@Test
void testConfig1() {
FrameworkModel frameworkModel = new FrameworkModel();
ApplicationModel applicationModel = frameworkModel.newApplication();
ModuleModel moduleModel = applicationModel.newModule();
System.setProperty(CLASS_DESERIALIZE_ALLOWED_LIST, "test.package1, test.package2, ,");
SerializeSecurityManager ssm = frameworkModel.getBeanFactory().getBean(SerializeSecurityManager.class);
SerializeSecurityConfigurator serializeSecurityConfigurator = new SerializeSecurityConfigurator(moduleModel);
serializeSecurityConfigurator.onAddClassLoader(moduleModel, Thread.currentThread().getContextClassLoader());
Assertions.assertTrue(ssm.getAllowedPrefix().contains("test.package1"));
Assertions.assertTrue(ssm.getAllowedPrefix().contains("test.package2"));
System.clearProperty(CommonConstants.CLASS_DESERIALIZE_ALLOWED_LIST);
frameworkModel.destroy();
}
@Test
void testConfig2() {
FrameworkModel frameworkModel = new FrameworkModel();
ApplicationModel applicationModel = frameworkModel.newApplication();
ModuleModel moduleModel = applicationModel.newModule();
System.setProperty(CLASS_DESERIALIZE_BLOCKED_LIST, "test.package1, test.package2, ,");
SerializeSecurityManager ssm = frameworkModel.getBeanFactory().getBean(SerializeSecurityManager.class);
SerializeSecurityConfigurator serializeSecurityConfigurator = new SerializeSecurityConfigurator(moduleModel);
serializeSecurityConfigurator.onAddClassLoader(moduleModel, Thread.currentThread().getContextClassLoader());
Assertions.assertTrue(ssm.getDisAllowedPrefix().contains("test.package1"));
Assertions.assertTrue(ssm.getDisAllowedPrefix().contains("test.package2"));
System.clearProperty(CommonConstants.CLASS_DESERIALIZE_BLOCK_ALL);
frameworkModel.destroy();
}
@Test
void testConfig3() {
FrameworkModel frameworkModel = new FrameworkModel();
ApplicationModel applicationModel = frameworkModel.newApplication();
ModuleModel moduleModel = applicationModel.newModule();
System.setProperty(CLASS_DESERIALIZE_ALLOWED_LIST, "test.package1, test.package2, ,");
System.setProperty(CLASS_DESERIALIZE_BLOCKED_LIST, "test.package1, test.package2, ,");
SerializeSecurityManager ssm = frameworkModel.getBeanFactory().getBean(SerializeSecurityManager.class);
SerializeSecurityConfigurator serializeSecurityConfigurator = new SerializeSecurityConfigurator(moduleModel);
serializeSecurityConfigurator.onAddClassLoader(moduleModel, Thread.currentThread().getContextClassLoader());
Assertions.assertTrue(ssm.getAllowedPrefix().contains("test.package1"));
Assertions.assertTrue(ssm.getAllowedPrefix().contains("test.package2"));
System.clearProperty(CommonConstants.CLASS_DESERIALIZE_ALLOWED_LIST);
System.clearProperty(CommonConstants.CLASS_DESERIALIZE_BLOCK_ALL);
frameworkModel.destroy();
}
@Test
void testSerializable1() {
FrameworkModel frameworkModel = new FrameworkModel();
ApplicationModel applicationModel = frameworkModel.newApplication();
ApplicationConfig applicationConfig = new ApplicationConfig("Test");
applicationConfig.setCheckSerializable(false);
applicationModel.getApplicationConfigManager().setApplication(applicationConfig);
ModuleModel moduleModel = applicationModel.newModule();
SerializeSecurityManager ssm = frameworkModel.getBeanFactory().getBean(SerializeSecurityManager.class);
SerializeSecurityConfigurator serializeSecurityConfigurator = new SerializeSecurityConfigurator(moduleModel);
serializeSecurityConfigurator.onAddClassLoader(moduleModel, Thread.currentThread().getContextClassLoader());
Assertions.assertFalse(ssm.isCheckSerializable());
frameworkModel.destroy();
}
@Test
void testSerializable2() {
FrameworkModel frameworkModel = new FrameworkModel();
ApplicationModel applicationModel = frameworkModel.newApplication();
ModuleModel moduleModel = applicationModel.newModule();
SerializeSecurityManager ssm = frameworkModel.getBeanFactory().getBean(SerializeSecurityManager.class);
SerializeSecurityConfigurator serializeSecurityConfigurator = new SerializeSecurityConfigurator(moduleModel);
serializeSecurityConfigurator.onAddClassLoader(moduleModel, Thread.currentThread().getContextClassLoader());
Assertions.assertTrue(ssm.isCheckSerializable());
frameworkModel.destroy();
}
@Test
void testRegister1() {
FrameworkModel frameworkModel = new FrameworkModel();
ApplicationModel applicationModel = frameworkModel.newApplication();
ModuleModel moduleModel = applicationModel.newModule();
SerializeSecurityManager ssm = frameworkModel.getBeanFactory().getBean(SerializeSecurityManager.class);
SerializeSecurityConfigurator serializeSecurityConfigurator = new SerializeSecurityConfigurator(moduleModel);
serializeSecurityConfigurator.onAddClassLoader(moduleModel, Thread.currentThread().getContextClassLoader());
serializeSecurityConfigurator.registerInterface(DemoService1.class);
Assertions.assertTrue(ssm.getAllowedPrefix().contains("com.service.DemoService1"));
Assertions.assertTrue(ssm.getAllowedPrefix().contains("com.pojo.Demo1"));
Assertions.assertTrue(ssm.getAllowedPrefix().contains("com.pojo.Demo2"));
Assertions.assertTrue(ssm.getAllowedPrefix().contains("com.pojo.Demo3"));
Assertions.assertTrue(ssm.getAllowedPrefix().contains("com.pojo.Demo4"));
Assertions.assertTrue(ssm.getAllowedPrefix().contains("com.pojo.Demo5"));
Assertions.assertTrue(ssm.getAllowedPrefix().contains("com.pojo.Demo6"));
Assertions.assertTrue(ssm.getAllowedPrefix().contains("com.pojo.Demo7"));
Assertions.assertTrue(ssm.getAllowedPrefix().contains("com.pojo.Demo8"));
Assertions.assertTrue(ssm.getAllowedPrefix().contains("com.pojo.Simple"));
Assertions.assertTrue(ssm.getAllowedPrefix().contains(List.class.getName()));
Assertions.assertTrue(ssm.getAllowedPrefix().contains(Set.class.getName()));
Assertions.assertTrue(ssm.getAllowedPrefix().contains(Map.class.getName()));
Assertions.assertTrue(ssm.getAllowedPrefix().contains(LinkedList.class.getName()));
Assertions.assertTrue(ssm.getAllowedPrefix().contains(Vector.class.getName()));
Assertions.assertTrue(ssm.getAllowedPrefix().contains(HashSet.class.getName()));
frameworkModel.destroy();
}
@Test
void testRegister2() {
FrameworkModel frameworkModel = new FrameworkModel();
ApplicationModel applicationModel = frameworkModel.newApplication();
ModuleModel moduleModel = applicationModel.newModule();
SerializeSecurityManager ssm = frameworkModel.getBeanFactory().getBean(SerializeSecurityManager.class);
SerializeSecurityConfigurator serializeSecurityConfigurator = new SerializeSecurityConfigurator(moduleModel);
serializeSecurityConfigurator.onAddClassLoader(moduleModel, Thread.currentThread().getContextClassLoader());
serializeSecurityConfigurator.registerInterface(DemoService2.class);
Assertions.assertTrue(ssm.getAllowedPrefix().contains("com.service.DemoService2"));
Assertions.assertTrue(ssm.getAllowedPrefix().contains("com.pojo.Demo1"));
Assertions.assertTrue(ssm.getAllowedPrefix().contains("com.pojo.Demo2"));
Assertions.assertTrue(ssm.getAllowedPrefix().contains("com.pojo.Demo3"));
Assertions.assertTrue(ssm.getAllowedPrefix().contains("com.pojo.Demo4"));
Assertions.assertTrue(ssm.getAllowedPrefix().contains("com.pojo.Demo5"));
Assertions.assertTrue(ssm.getAllowedPrefix().contains("com.pojo.Demo6"));
Assertions.assertTrue(ssm.getAllowedPrefix().contains("com.pojo.Demo7"));
Assertions.assertTrue(ssm.getAllowedPrefix().contains("com.pojo.Demo8"));
Assertions.assertTrue(ssm.getAllowedPrefix().contains("com.pojo.Simple"));
Assertions.assertTrue(ssm.getAllowedPrefix().contains(List.class.getName()));
Assertions.assertTrue(ssm.getAllowedPrefix().contains(Set.class.getName()));
Assertions.assertTrue(ssm.getAllowedPrefix().contains(Map.class.getName()));
Assertions.assertTrue(ssm.getAllowedPrefix().contains(LinkedList.class.getName()));
Assertions.assertTrue(ssm.getAllowedPrefix().contains(Vector.class.getName()));
Assertions.assertTrue(ssm.getAllowedPrefix().contains(HashSet.class.getName()));
frameworkModel.destroy();
}
@Test
void testRegister3() {
FrameworkModel frameworkModel = new FrameworkModel();
ApplicationModel applicationModel = frameworkModel.newApplication();
ModuleModel moduleModel = applicationModel.newModule();
ApplicationConfig applicationConfig = new ApplicationConfig("Test");
applicationConfig.setAutoTrustSerializeClass(false);
applicationModel.getApplicationConfigManager().setApplication(applicationConfig);
SerializeSecurityManager ssm = frameworkModel.getBeanFactory().getBean(SerializeSecurityManager.class);
SerializeSecurityConfigurator serializeSecurityConfigurator = new SerializeSecurityConfigurator(moduleModel);
serializeSecurityConfigurator.onAddClassLoader(moduleModel, Thread.currentThread().getContextClassLoader());
serializeSecurityConfigurator.registerInterface(DemoService1.class);
Assertions.assertFalse(ssm.getAllowedPrefix().contains("com.service.DemoService1"));
Assertions.assertFalse(ssm.getAllowedPrefix().contains("com.pojo.Demo1"));
Assertions.assertFalse(ssm.getAllowedPrefix().contains("com.pojo.Demo2"));
Assertions.assertFalse(ssm.getAllowedPrefix().contains("com.pojo.Demo3"));
Assertions.assertFalse(ssm.getAllowedPrefix().contains("com.pojo.Demo4"));
Assertions.assertFalse(ssm.getAllowedPrefix().contains("com.pojo.Demo5"));
Assertions.assertFalse(ssm.getAllowedPrefix().contains("com.pojo.Demo6"));
Assertions.assertFalse(ssm.getAllowedPrefix().contains("com.pojo.Demo7"));
Assertions.assertFalse(ssm.getAllowedPrefix().contains("com.pojo.Demo8"));
Assertions.assertFalse(ssm.getAllowedPrefix().contains("com.pojo.Simple"));
frameworkModel.destroy();
}
@Test
void testRegister4() {
FrameworkModel frameworkModel = new FrameworkModel();
ApplicationModel applicationModel = frameworkModel.newApplication();
ModuleModel moduleModel = applicationModel.newModule();
ApplicationConfig applicationConfig = new ApplicationConfig("Test");
applicationConfig.setTrustSerializeClassLevel(4);
applicationModel.getApplicationConfigManager().setApplication(applicationConfig);
SerializeSecurityManager ssm = frameworkModel.getBeanFactory().getBean(SerializeSecurityManager.class);
SerializeSecurityConfigurator serializeSecurityConfigurator = new SerializeSecurityConfigurator(moduleModel);
serializeSecurityConfigurator.onAddClassLoader(moduleModel, Thread.currentThread().getContextClassLoader());
serializeSecurityConfigurator.registerInterface(DemoService3.class);
Assertions.assertTrue(ssm.getAllowedPrefix().contains("com.service.deep1.deep2"));
frameworkModel.destroy();
}
@Test
void testRegister5() {
FrameworkModel frameworkModel = new FrameworkModel();
ApplicationModel applicationModel = frameworkModel.newApplication();
ModuleModel moduleModel = applicationModel.newModule();
ApplicationConfig applicationConfig = new ApplicationConfig("Test");
applicationConfig.setTrustSerializeClassLevel(10);
applicationModel.getApplicationConfigManager().setApplication(applicationConfig);
SerializeSecurityManager ssm = frameworkModel.getBeanFactory().getBean(SerializeSecurityManager.class);
SerializeSecurityConfigurator serializeSecurityConfigurator = new SerializeSecurityConfigurator(moduleModel);
serializeSecurityConfigurator.onAddClassLoader(moduleModel, Thread.currentThread().getContextClassLoader());
serializeSecurityConfigurator.registerInterface(DemoService3.class);
Assertions.assertTrue(ssm.getAllowedPrefix().contains("com.service.deep1.deep2.deep3.DemoService3"));
frameworkModel.destroy();
}
}

View File

@ -16,113 +16,115 @@
*/
package org.apache.dubbo.common.utils;
import org.apache.dubbo.rpc.model.FrameworkModel;
import com.service.DemoService1;
import com.service.DemoService2;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.Vector;
public class SerializeSecurityManagerTest {
class SerializeSecurityManagerTest {
@Test
public void test() {
SerializeSecurityManager ssm = new SerializeSecurityManager(FrameworkModel.defaultModel());
void testPrefix() {
TestAllowClassNotifyListener.setCount(0);
SerializeSecurityManager ssm = new SerializeSecurityManager();
ssm.registerListener(new TestAllowClassNotifyListener());
ssm.addToAllowed("java.util.HashMap");
ssm.addToAllowed("com.example.DemoInterface");
ssm.addToAllowed("com.sun.Interface1");
ssm.addToAllowed("com.sun.Interface2");
Assertions.assertTrue(ssm.getAllowedPrefix().contains("java.util.HashMap"));
Assertions.assertTrue(ssm.getAllowedPrefix().contains("com.example.DemoInterface"));
Assertions.assertTrue(ssm.getAllowedPrefix().contains("com.sun.Interface1"));
Assertions.assertFalse(ssm.getAllowedPrefix().contains("com.sun.Interface2"));
Assertions.assertTrue(ssm.getAllowedPrefix().contains("com.sun.Interface2"));
Assertions.assertEquals(ssm.getAllowedPrefix(), TestAllowClassNotifyListener.getPrefixList());
Assertions.assertEquals(ssm.getAllowedPrefix(), TestAllowClassNotifyListener.getAllowedList());
Assertions.assertEquals(7, TestAllowClassNotifyListener.getCount());
ssm.addToDisAllowed("com.sun.Interface");
Assertions.assertFalse(ssm.getAllowedPrefix().contains("com.sun.Interface1"));
Assertions.assertFalse(ssm.getAllowedPrefix().contains("com.sun.Interface2"));
Assertions.assertEquals(ssm.getDisAllowedPrefix(), TestAllowClassNotifyListener.getDisAllowedList());
Assertions.assertEquals(9, TestAllowClassNotifyListener.getCount());
ssm.addToAllowed("com.sun.Interface3");
Assertions.assertFalse(ssm.getAllowedPrefix().contains("com.sun.Interface3"));
Assertions.assertEquals(9, TestAllowClassNotifyListener.getCount());
ssm.addToAllowed("java.util.HashMap");
Assertions.assertEquals(9, TestAllowClassNotifyListener.getCount());
ssm.addToDisAllowed("com.sun.Interface");
Assertions.assertEquals(9, TestAllowClassNotifyListener.getCount());
ssm.addToAlwaysAllowed("com.sun.Interface3");
Assertions.assertTrue(ssm.getAllowedPrefix().contains("com.sun.Interface3"));
Assertions.assertEquals(10, TestAllowClassNotifyListener.getCount());
ssm.addToAlwaysAllowed("com.sun.Interface3");
Assertions.assertTrue(ssm.getAllowedPrefix().contains("com.sun.Interface3"));
Assertions.assertEquals(10, TestAllowClassNotifyListener.getCount());
}
@Test
public void addToAllow() {
SerializeSecurityManager ssm = new SerializeSecurityManager(FrameworkModel.defaultModel());
void testStatus1() {
SerializeSecurityManager ssm = new SerializeSecurityManager();
ssm.registerListener(new TestAllowClassNotifyListener());
Assertions.assertFalse(ssm.getAllowedPrefix().contains("com.sun.Interface2"));
Assertions.assertEquals(ssm.getAllowedPrefix(), TestAllowClassNotifyListener.getPrefixList());
ssm.addToAllow("com.sun.Interface2");
Assertions.assertFalse(ssm.getAllowedPrefix().contains("com.sun.Interface2"));
Assertions.assertEquals(ssm.getAllowedPrefix(), TestAllowClassNotifyListener.getPrefixList());
Assertions.assertEquals(AllowClassNotifyListener.DEFAULT_STATUS, ssm.getCheckStatus());
Assertions.assertEquals(AllowClassNotifyListener.DEFAULT_STATUS, TestAllowClassNotifyListener.getStatus());
ssm.addToAllow("java.util.Interface1");
Assertions.assertTrue(ssm.getAllowedPrefix().contains("java.util.Interface1"));
Assertions.assertEquals(ssm.getAllowedPrefix(), TestAllowClassNotifyListener.getPrefixList());
ssm.setCheckStatus(SerializeCheckStatus.STRICT);
Assertions.assertEquals(ssm.getCheckStatus(), TestAllowClassNotifyListener.getStatus());
Assertions.assertEquals(SerializeCheckStatus.STRICT, TestAllowClassNotifyListener.getStatus());
ssm.addToAllow("java.util.package.Interface1");
Assertions.assertTrue(ssm.getAllowedPrefix().contains("java.util.package.Interface1"));
Assertions.assertEquals(ssm.getAllowedPrefix(), TestAllowClassNotifyListener.getPrefixList());
ssm.setCheckStatus(SerializeCheckStatus.WARN);
Assertions.assertEquals(ssm.getCheckStatus(), TestAllowClassNotifyListener.getStatus());
Assertions.assertEquals(SerializeCheckStatus.WARN, TestAllowClassNotifyListener.getStatus());
ssm.addToAllow("com.example.Interface2");
Assertions.assertTrue(ssm.getAllowedPrefix().contains("com.example.Interface2"));
Assertions.assertEquals(ssm.getAllowedPrefix(), TestAllowClassNotifyListener.getPrefixList());
ssm.setCheckStatus(SerializeCheckStatus.STRICT);
Assertions.assertEquals(ssm.getCheckStatus(), TestAllowClassNotifyListener.getStatus());
Assertions.assertEquals(SerializeCheckStatus.WARN, TestAllowClassNotifyListener.getStatus());
ssm.addToAllow("com.example.package.Interface1");
Assertions.assertTrue(ssm.getAllowedPrefix().contains("com.example.package"));
Assertions.assertEquals(ssm.getAllowedPrefix(), TestAllowClassNotifyListener.getPrefixList());
ssm.setCheckStatus(SerializeCheckStatus.DISABLE);
Assertions.assertEquals(ssm.getCheckStatus(), TestAllowClassNotifyListener.getStatus());
Assertions.assertEquals(SerializeCheckStatus.DISABLE, TestAllowClassNotifyListener.getStatus());
ssm.setCheckStatus(SerializeCheckStatus.STRICT);
Assertions.assertEquals(ssm.getCheckStatus(), TestAllowClassNotifyListener.getStatus());
Assertions.assertEquals(SerializeCheckStatus.DISABLE, TestAllowClassNotifyListener.getStatus());
ssm.setCheckStatus(SerializeCheckStatus.WARN);
Assertions.assertEquals(ssm.getCheckStatus(), TestAllowClassNotifyListener.getStatus());
Assertions.assertEquals(SerializeCheckStatus.DISABLE, TestAllowClassNotifyListener.getStatus());
}
@Test
public void testRegister1() {
SerializeSecurityManager ssm = new SerializeSecurityManager(FrameworkModel.defaultModel());
void testStatus2() {
SerializeSecurityManager ssm = new SerializeSecurityManager();
ssm.setCheckStatus(SerializeCheckStatus.STRICT);
ssm.registerListener(new TestAllowClassNotifyListener());
ssm.registerInterface(DemoService1.class);
Assertions.assertTrue(ssm.getAllowedPrefix().contains("com.service.DemoService1"));
Assertions.assertTrue(ssm.getAllowedPrefix().contains("com.pojo.Demo1"));
Assertions.assertTrue(ssm.getAllowedPrefix().contains("com.pojo.Demo2"));
Assertions.assertTrue(ssm.getAllowedPrefix().contains("com.pojo.Demo3"));
Assertions.assertTrue(ssm.getAllowedPrefix().contains("com.pojo.Demo4"));
Assertions.assertTrue(ssm.getAllowedPrefix().contains("com.pojo.Demo5"));
Assertions.assertTrue(ssm.getAllowedPrefix().contains("com.pojo.Demo6"));
Assertions.assertTrue(ssm.getAllowedPrefix().contains("com.pojo.Demo7"));
Assertions.assertTrue(ssm.getAllowedPrefix().contains("com.pojo.Demo8"));
Assertions.assertTrue(ssm.getAllowedPrefix().contains("com.pojo.Simple"));
Assertions.assertTrue(ssm.getAllowedPrefix().contains(List.class.getName()));
Assertions.assertTrue(ssm.getAllowedPrefix().contains(Set.class.getName()));
Assertions.assertTrue(ssm.getAllowedPrefix().contains(Map.class.getName()));
Assertions.assertTrue(ssm.getAllowedPrefix().contains(LinkedList.class.getName()));
Assertions.assertTrue(ssm.getAllowedPrefix().contains(Vector.class.getName()));
Assertions.assertTrue(ssm.getAllowedPrefix().contains(HashSet.class.getName()));
Assertions.assertEquals(ssm.getAllowedPrefix(), TestAllowClassNotifyListener.getPrefixList());
Assertions.assertEquals(ssm.getCheckStatus(), TestAllowClassNotifyListener.getStatus());
Assertions.assertEquals(SerializeCheckStatus.STRICT, TestAllowClassNotifyListener.getStatus());
}
@Test
public void testRegister2() {
SerializeSecurityManager ssm = new SerializeSecurityManager(FrameworkModel.defaultModel());
void testSerializable() {
SerializeSecurityManager ssm = new SerializeSecurityManager();
ssm.registerListener(new TestAllowClassNotifyListener());
ssm.registerInterface(DemoService2.class);
Assertions.assertTrue(ssm.getAllowedPrefix().contains("com.service.DemoService2"));
Assertions.assertTrue(ssm.getAllowedPrefix().contains("com.pojo.Demo1"));
Assertions.assertTrue(ssm.getAllowedPrefix().contains("com.pojo.Demo2"));
Assertions.assertTrue(ssm.getAllowedPrefix().contains("com.pojo.Demo3"));
Assertions.assertTrue(ssm.getAllowedPrefix().contains("com.pojo.Demo4"));
Assertions.assertTrue(ssm.getAllowedPrefix().contains("com.pojo.Demo5"));
Assertions.assertTrue(ssm.getAllowedPrefix().contains("com.pojo.Demo6"));
Assertions.assertTrue(ssm.getAllowedPrefix().contains("com.pojo.Demo7"));
Assertions.assertTrue(ssm.getAllowedPrefix().contains("com.pojo.Demo8"));
Assertions.assertTrue(ssm.getAllowedPrefix().contains("com.pojo.Simple"));
Assertions.assertTrue(ssm.isCheckSerializable());
Assertions.assertTrue(TestAllowClassNotifyListener.isCheckSerializable());
Assertions.assertTrue(ssm.getAllowedPrefix().contains(List.class.getName()));
Assertions.assertTrue(ssm.getAllowedPrefix().contains(Set.class.getName()));
Assertions.assertTrue(ssm.getAllowedPrefix().contains(Map.class.getName()));
Assertions.assertTrue(ssm.getAllowedPrefix().contains(LinkedList.class.getName()));
Assertions.assertTrue(ssm.getAllowedPrefix().contains(Vector.class.getName()));
Assertions.assertTrue(ssm.getAllowedPrefix().contains(HashSet.class.getName()));
ssm.setCheckSerializable(true);
Assertions.assertTrue(ssm.isCheckSerializable());
Assertions.assertTrue(TestAllowClassNotifyListener.isCheckSerializable());
Assertions.assertEquals(ssm.getAllowedPrefix(), TestAllowClassNotifyListener.getPrefixList());
ssm.setCheckSerializable(false);
Assertions.assertFalse(ssm.isCheckSerializable());
Assertions.assertFalse(TestAllowClassNotifyListener.isCheckSerializable());
ssm.setCheckSerializable(true);
Assertions.assertFalse(ssm.isCheckSerializable());
Assertions.assertFalse(TestAllowClassNotifyListener.isCheckSerializable());
}
}

View File

@ -17,17 +17,58 @@
package org.apache.dubbo.common.utils;
import java.util.Set;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;
public class TestAllowClassNotifyListener implements AllowClassNotifyListener {
private final static AtomicReference<Set<String>> prefixList = new AtomicReference<>();
private final static AtomicReference<SerializeCheckStatus> status = new AtomicReference<>();
private final static AtomicReference<Set<String>> allowedList = new AtomicReference<>();
private final static AtomicReference<Set<String>> disAllowedList = new AtomicReference<>();
private final static AtomicBoolean checkSerializable = new AtomicBoolean();
private final static AtomicInteger count = new AtomicInteger(0);
@Override
public void notify(SerializeCheckStatus status, Set<String> prefixList) {
TestAllowClassNotifyListener.prefixList.set(prefixList);
public void notifyPrefix(Set<String> allowedList, Set<String> disAllowedList) {
TestAllowClassNotifyListener.allowedList.set(allowedList);
TestAllowClassNotifyListener.disAllowedList.set(disAllowedList);
count.incrementAndGet();
}
public static Set<String> getPrefixList() {
return prefixList.get();
@Override
public void notifyCheckStatus(SerializeCheckStatus status) {
TestAllowClassNotifyListener.status.set(status);
count.incrementAndGet();
}
@Override
public void notifyCheckSerializable(boolean checkSerializable) {
TestAllowClassNotifyListener.checkSerializable.set(checkSerializable);
count.incrementAndGet();
}
public static SerializeCheckStatus getStatus() {
return status.get();
}
public static Set<String> getAllowedList() {
return allowedList.get();
}
public static Set<String> getDisAllowedList() {
return disAllowedList.get();
}
public static boolean isCheckSerializable() {
return checkSerializable.get();
}
public static int getCount() {
return count.get();
}
public static void setCount(int count) {
TestAllowClassNotifyListener.count.set(count);
}
}

View File

@ -16,5 +16,6 @@
# limitations under the License.
#
#
com.example.DemoInterface
com.sun.Interface1

View File

@ -0,0 +1,20 @@
#
#
# Licensed to the Apache Software Foundation (ASF) under one or more
# contributor license agreements. See the NOTICE file distributed with
# this work for additional information regarding copyright ownership.
# The ASF licenses this file to You under the Apache License, Version 2.0
# (the "License"); you may not use this file except in compliance with
# the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
#
com.exampletest.DemoInterface

View File

@ -1135,9 +1135,32 @@
}
]
},
{
"name": "org.apache.dubbo.common.utils.SerializeSecurityConfigurator",
"allPublicMethods": true,
"methods": [
{
"name": "<init>",
"parameterTypes": [
"org.apache.dubbo.rpc.model.ModuleModel"
]
}
]
},
{
"name": "org.apache.dubbo.common.utils.SerializeSecurityManager",
"allPublicMethods": true,
"methods": [
{
"name": "<init>",
"parameterTypes": [
]
}
]
},
{
"name": "org.apache.dubbo.common.utils.DefaultSerializeClassChecker",
"allPublicMethods": true,
"methods": [
{
"name": "<init>",

View File

@ -16,9 +16,14 @@
*/
package org.apache.dubbo.rpc.protocol;
import java.util.List;
import java.util.Optional;
import org.apache.dubbo.common.URL;
import org.apache.dubbo.common.extension.Activate;
import org.apache.dubbo.common.utils.SerializeSecurityManager;
import org.apache.dubbo.common.logger.ErrorTypeAwareLogger;
import org.apache.dubbo.common.logger.LoggerFactory;
import org.apache.dubbo.common.utils.SerializeSecurityConfigurator;
import org.apache.dubbo.rpc.Exporter;
import org.apache.dubbo.rpc.Invoker;
import org.apache.dubbo.rpc.Protocol;
@ -29,12 +34,13 @@ import org.apache.dubbo.rpc.model.ScopeModelUtil;
import org.apache.dubbo.rpc.model.ServiceDescriptor;
import org.apache.dubbo.rpc.model.ServiceModel;
import java.util.List;
import java.util.Optional;
import static org.apache.dubbo.common.constants.LoggerCodeConstants.INTERNAL_ERROR;
@Activate(order = 200)
public class ProtocolSecurityWrapper implements Protocol {
private final Protocol protocol;
private static final ErrorTypeAwareLogger logger = LoggerFactory.getErrorTypeAwareLogger(ProtocolSecurityWrapper.class);
public ProtocolSecurityWrapper(Protocol protocol) {
if (protocol == null) {
@ -50,31 +56,39 @@ public class ProtocolSecurityWrapper implements Protocol {
@Override
public <T> Exporter<T> export(Invoker<T> invoker) throws RpcException {
ServiceModel serviceModel = invoker.getUrl().getServiceModel();
ScopeModel scopeModel = invoker.getUrl().getScopeModel();
Optional.ofNullable(serviceModel)
.map(ServiceModel::getServiceModel)
.map(ServiceDescriptor::getServiceInterfaceClass)
.ifPresent((interfaceClass) -> {
SerializeSecurityManager serializeSecurityManager = ScopeModelUtil.getFrameworkModel(scopeModel)
.getBeanFactory().getBean(SerializeSecurityManager.class);
serializeSecurityManager.registerInterface(interfaceClass);
});
try {
ServiceModel serviceModel = invoker.getUrl().getServiceModel();
ScopeModel scopeModel = invoker.getUrl().getScopeModel();
Optional.ofNullable(serviceModel)
.map(ServiceModel::getServiceModel)
.map(ServiceDescriptor::getServiceInterfaceClass)
.ifPresent((interfaceClass) -> {
SerializeSecurityConfigurator serializeSecurityConfigurator = ScopeModelUtil.getModuleModel(scopeModel)
.getBeanFactory().getBean(SerializeSecurityConfigurator.class);
serializeSecurityConfigurator.registerInterface(interfaceClass);
});
} catch (Throwable t) {
logger.error(INTERNAL_ERROR, "", "", "Failed to register interface for security check", t);
}
return protocol.export(invoker);
}
@Override
public <T> Invoker<T> refer(Class<T> type, URL url) throws RpcException {
ServiceModel serviceModel = url.getServiceModel();
ScopeModel scopeModel = url.getScopeModel();
SerializeSecurityManager serializeSecurityManager = ScopeModelUtil.getFrameworkModel(scopeModel)
.getBeanFactory().getBean(SerializeSecurityManager.class);
try {
ServiceModel serviceModel = url.getServiceModel();
ScopeModel scopeModel = url.getScopeModel();
SerializeSecurityConfigurator serializeSecurityConfigurator = ScopeModelUtil.getModuleModel(scopeModel)
.getBeanFactory().getBean(SerializeSecurityConfigurator.class);
Optional.ofNullable(serviceModel)
.map(ServiceModel::getServiceModel)
.map(ServiceDescriptor::getServiceInterfaceClass)
.ifPresent(serializeSecurityManager::registerInterface);
serializeSecurityManager.registerInterface(type);
Optional.ofNullable(serviceModel)
.map(ServiceModel::getServiceModel)
.map(ServiceDescriptor::getServiceInterfaceClass)
.ifPresent(serializeSecurityConfigurator::registerInterface);
serializeSecurityConfigurator.registerInterface(type);
} catch (Throwable t) {
logger.error(INTERNAL_ERROR, "", "", "Failed to register interface for security check", t);
}
return protocol.refer(type, url);
}

View File

@ -37,19 +37,43 @@ import static org.apache.dubbo.common.constants.LoggerCodeConstants.PROTOCOL_UNT
import static org.apache.dubbo.common.utils.SerializeCheckStatus.STRICT;
public class Fastjson2SecurityManager implements AllowClassNotifyListener {
private Filter securityFilter = new Handler(AllowClassNotifyListener.DEFAULT_STATUS, new String[0]);
private Filter securityFilter = new Handler(AllowClassNotifyListener.DEFAULT_STATUS, true, new String[0], new ConcurrentHashSet<>());
private static final ErrorTypeAwareLogger logger = LoggerFactory.getErrorTypeAwareLogger(Fastjson2SecurityManager.class);
private static final Set<String> warnedClasses = new ConcurrentHashSet<>(1);
private volatile SerializeCheckStatus status = AllowClassNotifyListener.DEFAULT_STATUS;
private volatile boolean checkSerializable = true;
private volatile Set<String> allowedList = new ConcurrentHashSet<>(1);
private volatile Set<String> disAllowedList = new ConcurrentHashSet<>(1);
public Fastjson2SecurityManager(FrameworkModel frameworkModel) {
SerializeSecurityManager securityManager = frameworkModel.getBeanFactory().getOrRegisterBean(SerializeSecurityManager.class);
securityManager.registerListener(this);
}
public void notify(SerializeCheckStatus status, Set<String> prefixList) {
this.securityFilter = new Handler(status, prefixList.toArray(new String[0]));
@Override
public synchronized void notifyPrefix(Set<String> allowedList, Set<String> disAllowedList) {
this.allowedList = allowedList;
this.disAllowedList = disAllowedList;
this.securityFilter = new Handler(this.status, this.checkSerializable, this.allowedList.toArray(new String[0]), this.disAllowedList);
}
@Override
public synchronized void notifyCheckStatus(SerializeCheckStatus status) {
this.status = status;
this.securityFilter = new Handler(this.status, this.checkSerializable, this.allowedList.toArray(new String[0]), this.disAllowedList);
}
@Override
public synchronized void notifyCheckSerializable(boolean checkSerializable) {
this.checkSerializable = checkSerializable;
this.securityFilter = new Handler(this.status, this.checkSerializable, this.allowedList.toArray(new String[0]), this.disAllowedList);
}
public Filter getSecurityFilter() {
@ -60,9 +84,15 @@ public class Fastjson2SecurityManager implements AllowClassNotifyListener {
final SerializeCheckStatus status;
final Map<String, Class<?>> classCache = new ConcurrentHashMap<>(16, 0.75f, 1);
public Handler(SerializeCheckStatus status, String[] acceptNames) {
final Set<String> disAllowedList;
final boolean checkSerializable;
public Handler(SerializeCheckStatus status, boolean checkSerializable, String[] acceptNames, Set<String> disAllowedList) {
super(true, acceptNames);
this.status = status;
this.checkSerializable = checkSerializable;
this.disAllowedList = disAllowedList;
}
@Override
@ -103,9 +133,27 @@ public class Fastjson2SecurityManager implements AllowClassNotifyListener {
return null;
}
public boolean checkIfDisAllow(String typeName) {
return disAllowedList.stream().anyMatch(typeName::startsWith);
}
public boolean isCheckSerializable() {
return checkSerializable;
}
public Class<?> loadClassDirectly(String typeName) {
Class<?> clazz = classCache.get(typeName);
if (clazz == null && checkIfDisAllow(typeName)) {
clazz = DenyClass.class;
String msg = "[Serialization Security] Serialized class " + typeName + " is in disAllow list. " +
"Current mode is `WARN`, will disallow to deserialize it by default. " +
"Please add it into security/serialize.allowlist or follow FAQ to configure it.";
if (warnedClasses.add(typeName)) {
logger.error(PROTOCOL_UNTRUSTED_SERIALIZE_CLASS, "", "", msg);
}
}
if (clazz == null) {
clazz = TypeUtils.getMapping(typeName);
}
@ -121,8 +169,16 @@ public class Fastjson2SecurityManager implements AllowClassNotifyListener {
}
}
if (clazz == DenyClass.class) {
return null;
}
return clazz;
}
}
private static class DenyClass {
// To indicate that the target class has been reject
}
}

View File

@ -16,14 +16,15 @@
*/
package org.apache.dubbo.common.serialize.hessian2;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import org.apache.dubbo.common.utils.DefaultSerializeClassChecker;
import org.apache.dubbo.common.utils.StringUtils;
import org.apache.dubbo.rpc.model.FrameworkModel;
import com.alibaba.com.caucho.hessian.io.SerializerFactory;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
public class Hessian2FactoryManager {
String WHITELIST = "dubbo.application.hessian2.whitelist";
@ -32,10 +33,10 @@ public class Hessian2FactoryManager {
private volatile SerializerFactory SYSTEM_SERIALIZER_FACTORY;
private final Map<ClassLoader, SerializerFactory> CL_2_SERIALIZER_FACTORY = new ConcurrentHashMap<>();
private final Hessian2AllowClassManager hessian2AllowClassManager;
private final DefaultSerializeClassChecker defaultSerializeClassChecker;
public Hessian2FactoryManager(FrameworkModel frameworkModel) {
hessian2AllowClassManager = new Hessian2AllowClassManager(frameworkModel);
defaultSerializeClassChecker = frameworkModel.getBeanFactory().getOrRegisterBean(DefaultSerializeClassChecker.class);
}
public SerializerFactory getSerializerFactory(ClassLoader classLoader) {
@ -73,14 +74,14 @@ public class Hessian2FactoryManager {
}
private SerializerFactory createDefaultSerializerFactory() {
Hessian2SerializerFactory hessian2SerializerFactory = new Hessian2SerializerFactory(hessian2AllowClassManager);
Hessian2SerializerFactory hessian2SerializerFactory = new Hessian2SerializerFactory(defaultSerializeClassChecker);
hessian2SerializerFactory.setAllowNonSerializable(Boolean.parseBoolean(System.getProperty("dubbo.hessian.allowNonSerializable", "false")));
hessian2SerializerFactory.getClassFactory().allow("org.apache.dubbo.*");
return hessian2SerializerFactory;
}
public SerializerFactory createWhiteListSerializerFactory() {
SerializerFactory serializerFactory = new Hessian2SerializerFactory(hessian2AllowClassManager);
SerializerFactory serializerFactory = new Hessian2SerializerFactory(defaultSerializeClassChecker);
String whiteList = System.getProperty(WHITELIST);
if ("true".equals(whiteList)) {
serializerFactory.getClassFactory().setWhitelist(true);

View File

@ -16,18 +16,20 @@
*/
package org.apache.dubbo.common.serialize.hessian2;
import org.apache.dubbo.common.utils.DefaultSerializeClassChecker;
import com.alibaba.com.caucho.hessian.io.SerializerFactory;
public class Hessian2SerializerFactory extends SerializerFactory {
private Hessian2AllowClassManager hessian2AllowClassManager;
private final DefaultSerializeClassChecker defaultSerializeClassChecker;
public Hessian2SerializerFactory(Hessian2AllowClassManager hessian2AllowClassManager) {
this.hessian2AllowClassManager = hessian2AllowClassManager;
public Hessian2SerializerFactory(DefaultSerializeClassChecker defaultSerializeClassChecker) {
this.defaultSerializeClassChecker = defaultSerializeClassChecker;
}
@Override
public Class<?> loadSerializedClass(String className) throws ClassNotFoundException {
return hessian2AllowClassManager.loadClass(getClassLoader(), className);
return defaultSerializeClassChecker.loadClass(getClassLoader(), className);
}
}