Skip to content

Commit

Permalink
Add serialize class checker (apache#7436)
Browse files Browse the repository at this point in the history
  • Loading branch information
AlbumenJ committed Mar 26, 2021
1 parent 9b7575a commit 4d3a318
Show file tree
Hide file tree
Showing 9 changed files with 463 additions and 3 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
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;
Expand Down Expand Up @@ -464,6 +465,7 @@ public static Class<?> name2Class(ClassLoader loader, String name) throws ClassN
if (isReferenceType(name)) {
name = name.substring(1, name.length() - 1);
}
SerializeClassChecker.getInstance().validateClass(name);
return Class.forName(name, false, loader);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -371,4 +371,14 @@ public interface CommonConstants {
String CLASSPATH_URL_PREFIX = "classpath:";

String DEFAULT_VERSION = "0.0.0";

String CLASS_DESERIALIZE_BLOCK_ALL = "dubbo.security.serialize.blockAllClassExceptAllow";

String CLASS_DESERIALIZE_ALLOWED_LIST = "dubbo.security.serialize.allowedClassList";

String CLASS_DESERIALIZE_BLOCKED_LIST = "dubbo.security.serialize.blockedClassList";

String ENABLE_NATIVE_JAVA_GENERIC_SERIALIZE = "dubbo.security.serialize.generic.native-java-enable";

String SERIALIZE_BLOCKED_LIST_FILE_PATH = "security/serialize.blockedlist";
}
Original file line number Diff line number Diff line change
Expand Up @@ -387,6 +387,7 @@ private static Object realize0(Object pojo, Class<?> type, Type genericType, fin
if (pojo instanceof Map<?, ?> && type != null) {
Object className = ((Map<Object, Object>) pojo).get("class");
if (className instanceof String) {
SerializeClassChecker.getInstance().validateClass((String) className);
try {
type = ClassUtils.forName((String) className);
} catch (ClassNotFoundException e) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@
/*
* 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.Logger;
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;

public class SerializeClassChecker {
private static final Logger logger = LoggerFactory.getLogger(SerializeClassChecker.class);

private static volatile SerializeClassChecker INSTANCE = null;

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 blockAllClassExceptAllow = System.getProperty(CommonConstants.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(CommonConstants.SERIALIZE_BLOCKED_LIST_FILE_PATH));
} else {
lines = IOUtils.readLines(ClassLoader.getSystemResourceAsStream(CommonConstants.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("Failed to load blocked class list! Will ignore default blocked list.", e);
}

String allowedClassList = System.getProperty(CommonConstants.CLASS_DESERIALIZE_ALLOWED_LIST, "").trim().toLowerCase(Locale.ROOT);
String blockedClassList = System.getProperty(CommonConstants.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
*
* @throws IllegalArgumentException if class is blocked
* @param name class name ( all are convert to lower case )
*/
public void validateClass(String name) {
name = name.toLowerCase(Locale.ROOT);
if (CACHE == CLASS_ALLOW_LFU_CACHE.get(name)) {
return;
}

if (CACHE == CLASS_BLOCK_LFU_CACHE.get(name)) {
error(name);
}

for (String allowedPrefix : CLASS_DESERIALIZE_ALLOWED_SET) {
if (name.startsWith(allowedPrefix)) {
CLASS_ALLOW_LFU_CACHE.put(name, CACHE);
return;
}
}

for (String blockedPrefix : CLASS_DESERIALIZE_BLOCKED_SET) {
if (BLOCK_ALL_CLASS_EXCEPT_ALLOW || name.startsWith(blockedPrefix)) {
CLASS_BLOCK_LFU_CACHE.put(name, CACHE);
error(name);
}
}

CLASS_ALLOW_LFU_CACHE.put(name, CACHE);
}

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 `" + CommonConstants.CLASS_DESERIALIZE_ALLOWED_LIST +
"` as a system environment property.";
if (counter.incrementAndGet() % 1000 == 0 || counter.get() < 100) {
logger.error(notice);
}
throw new IllegalArgumentException(notice);
}

}
167 changes: 167 additions & 0 deletions dubbo-common/src/main/resources/security/serialize.blockedlist
Original file line number Diff line number Diff line change
@@ -0,0 +1,167 @@
#
#
# 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.
#
#
aj.org.objectweb.asm.
br.com.anteros.
ch.qos.logback.
clojure.core$constantly
clojure.main$eval_opt
com.alibaba.citrus.springext.support.parser.abstractnamedproxybeandefinitionparser$proxytargetfactory
com.alibaba.citrus.springext.util.springextutil.abstractproxy
com.alibaba.druid.pool.druiddatasource
com.alibaba.druid.stat.jdbcdatasourcestat
com.alibaba.fastjson.annotation
com.alipay.custrelation.service.model.redress.pair
com.caucho.
com.ibatis.
com.mchange
com.mysql.cj.jdbc.admin.
com.mysql.cj.jdbc.mysqlconnectionpooldatasource
com.mysql.cj.jdbc.mysqldatasource
com.mysql.cj.jdbc.mysqlxadatasource
com.mysql.cj.log.
com.p6spy.engine.
com.rometools.rome.feed.impl.equalsbean
com.rometools.rome.feed.impl.tostringbean
com.sun.
com.taobao.eagleeye.wrapper
com.zaxxer.hikari.
flex.messaging.util.concurrent.
java.awt.i
java.awt.p
java.beans.expression
java.io.closeable
java.io.serializable
java.lang.autocloseable
java.lang.class
java.lang.cloneable
java.lang.iterable
java.lang.object
java.lang.readable
java.lang.runnable
java.lang.thread
java.lang.unixprocess
java.net.inetaddress
java.net.socket
java.net.url
java.rmi
java.security.signedobject
java.util.collection
java.util.eventlistener
java.util.jar.
java.util.logging.
java.util.prefs.
java.util.serviceloader$lazyiterator
javassist.
javax.activation.
javax.imageio.imageio$containsfilter
javax.imageio.spi.serviceregistry
javax.management.
javax.naming.
javax.net.
javax.print.
javax.script.
javax.sound.
javax.swing.j
javax.tools.
javax.xml
jdk.internal.
jodd.db.connection.
junit.
net.bytebuddy.dynamic.loading.bytearrayclassloader
net.sf.cglib.
net.sf.ehcache.hibernate.
net.sf.ehcache.transaction.manager.
oracle.jdbc.
oracle.jms.aq
oracle.net
org.aoju.bus.proxy.provider.
org.apache.activemq.activemqconnectionfactory
org.apache.activemq.activemqxaconnectionfactory
org.apache.activemq.jms.pool.
org.apache.activemq.pool.
org.apache.activemq.spring.
org.apache.aries.transaction.
org.apache.axis2.jaxws.spi.handler.
org.apache.axis2.transport.jms.
org.apache.bcel
org.apache.carbondata.core.scan.expression.expressionresult
org.apache.catalina.
org.apache.cocoon.
org.apache.commons.beanutils
org.apache.commons.collections.comparators.
org.apache.commons.collections.functors
org.apache.commons.collections.functors.
org.apache.commons.collections.transformer
org.apache.commons.collections4.comparators
org.apache.commons.collections4.functors
org.apache.commons.collections4.transformer
org.apache.commons.configuration
org.apache.commons.dbcp
org.apache.commons.fileupload
org.apache.commons.jelly.
org.apache.commons.logging.
org.apache.commons.proxy.
org.apache.cxf.jaxrs.provider.
org.apache.hadoop.shaded.com.zaxxer.hikari.
org.apache.http.auth.
org.apache.http.conn.
org.apache.http.cookie.
org.apache.http.impl.
org.apache.ibatis.datasource
org.apache.ibatis.executor.
org.apache.ibatis.javassist.
org.apache.ibatis.ognl.
org.apache.ibatis.parsing.
org.apache.ibatis.reflection.
org.apache.ibatis.scripting.
org.apache.ignite.cache.jta.
org.apache.log4j.
org.apache.logging.
org.apache.myfaces.context.servlet
org.apache.openjpa.ee.
org.apache.shiro.jndi.
org.apache.shiro.realm.
org.apache.tomcat
org.apache.wicket.util
org.apache.xalan
org.apache.xbean.
org.apache.xpath.xpathcontext
org.codehaus.groovy.runtime
org.codehaus.jackson.
org.eclipse.jetty.
org.geotools.filter.constantexpression
org.h2.jdbcx.
org.h2.server.
org.hibernate
org.javasimon.
org.jaxen.
org.jboss
org.jdom.
org.jdom2.transform.
org.logicalcobwebs.
org.mortbay.jetty.
org.mozilla.javascript
org.objectweb.asm.
org.osjava.sj.
org.python.core
org.quartz.
org.slf4j.
org.springframework.
org.yaml.snakeyaml.tokens.directivetoken
sun.rmi.server.unicastref
Loading

0 comments on commit 4d3a318

Please sign in to comment.