[3.0]Fix security problem (#7779)

This commit is contained in:
Wu Zhiguo 2021-05-22 13:56:33 +08:00 committed by GitHub
parent 63d319b60e
commit 5eedd4970a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
28 changed files with 822 additions and 56 deletions

View File

@ -16,26 +16,25 @@
*/
package org.apache.dubbo.rpc.cluster.configurator.parser;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONValidator;
import org.apache.dubbo.common.URL;
import org.apache.dubbo.common.utils.CollectionUtils;
import org.apache.dubbo.common.utils.StringUtils;
import org.apache.dubbo.rpc.cluster.configurator.parser.model.ConfigItem;
import org.apache.dubbo.rpc.cluster.configurator.parser.model.ConfiguratorConfig;
import org.yaml.snakeyaml.TypeDescription;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONValidator;
import org.yaml.snakeyaml.Yaml;
import org.yaml.snakeyaml.constructor.Constructor;
import org.yaml.snakeyaml.constructor.SafeConstructor;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import static org.apache.dubbo.rpc.cluster.Constants.OVERRIDE_PROVIDERS_KEY;
import static org.apache.dubbo.common.constants.CommonConstants.ANYHOST_VALUE;
import static org.apache.dubbo.common.constants.RegistryConstants.APP_DYNAMIC_CONFIGURATORS_CATEGORY;
import static org.apache.dubbo.common.constants.RegistryConstants.DYNAMIC_CONFIGURATORS_CATEGORY;
import static org.apache.dubbo.rpc.cluster.Constants.OVERRIDE_PROVIDERS_KEY;
/**
* Config parser
@ -72,14 +71,10 @@ public class ConfigParser {
return urls;
}
private static <T> T parseObject(String rawConfig) {
Constructor constructor = new Constructor(ConfiguratorConfig.class);
TypeDescription itemDescription = new TypeDescription(ConfiguratorConfig.class);
itemDescription.addPropertyParameters("items", ConfigItem.class);
constructor.addTypeDescription(itemDescription);
Yaml yaml = new Yaml(constructor);
return yaml.load(rawConfig);
private static ConfiguratorConfig parseObject(String rawConfig) {
Yaml yaml = new Yaml(new SafeConstructor());
Map<String, Object> map = yaml.load(rawConfig);
return ConfiguratorConfig.parseFromMap(map);
}
private static List<URL> serviceItemToUrls(ConfigItem item, ConfiguratorConfig config) {

View File

@ -18,6 +18,7 @@ package org.apache.dubbo.rpc.cluster.configurator.parser.model;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
/**
*
@ -37,6 +38,50 @@ public class ConfigItem {
private Map<String, String> parameters;
private String side;
@SuppressWarnings("unchecked")
public static ConfigItem parseFromMap(Map<String, Object> map) {
ConfigItem configItem = new ConfigItem();
configItem.setType((String) map.get("type"));
Object enabled = map.get("enabled");
if (enabled != null) {
configItem.setEnabled(Boolean.parseBoolean(enabled.toString()));
}
Object addresses = map.get("addresses");
if (addresses != null && List.class.isAssignableFrom(addresses.getClass())) {
configItem.setAddresses(((List<Object>) addresses).stream()
.map(String::valueOf).collect(Collectors.toList()));
}
Object providerAddresses = map.get("providerAddresses");
if (providerAddresses != null && List.class.isAssignableFrom(providerAddresses.getClass())) {
configItem.setProviderAddresses(((List<Object>) providerAddresses).stream()
.map(String::valueOf).collect(Collectors.toList()));
}
Object services = map.get("services");
if (services != null && List.class.isAssignableFrom(services.getClass())) {
configItem.setServices(((List<Object>) services).stream()
.map(String::valueOf).collect(Collectors.toList()));
}
Object applications = map.get("applications");
if (applications != null && List.class.isAssignableFrom(applications.getClass())) {
configItem.setApplications(((List<Object>) applications).stream()
.map(String::valueOf).collect(Collectors.toList()));
}
Object parameters = map.get("parameters");
if (parameters != null && Map.class.isAssignableFrom(parameters.getClass())) {
configItem.setParameters(((Map<String, Object>) parameters).entrySet()
.stream().collect(Collectors.toMap(Map.Entry::getKey, entry -> entry.getValue().toString())));
}
configItem.setSide((String) map.get("side"));
return configItem;
}
public String getType() {
return type;
}

View File

@ -17,6 +17,8 @@
package org.apache.dubbo.rpc.cluster.configurator.parser.model;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
/**
*
@ -31,6 +33,26 @@ public class ConfiguratorConfig {
private Boolean enabled = true;
private List<ConfigItem> configs;
@SuppressWarnings("unchecked")
public static ConfiguratorConfig parseFromMap(Map<String, Object> map) {
ConfiguratorConfig configuratorConfig = new ConfiguratorConfig();
configuratorConfig.setConfigVersion((String) map.get("configVersion"));
configuratorConfig.setScope((String) map.get("scope"));
configuratorConfig.setKey((String) map.get("key"));
Object enabled = map.get("enabled");
if (enabled != null) {
configuratorConfig.setEnabled(Boolean.parseBoolean(enabled.toString()));
}
Object configs = map.get("configs");
if (configs != null && List.class.isAssignableFrom(configs.getClass())) {
configuratorConfig.setConfigs(((List<Map<String, Object>>) configs).stream()
.map(ConfigItem::parseFromMap).collect(Collectors.toList()));
}
return configuratorConfig;
}
public String getConfigVersion() {
return configVersion;

View File

@ -16,6 +16,8 @@
*/
package org.apache.dubbo.rpc.cluster.router;
import java.util.Map;
/**
* TODO Extract more code here if necessary
*/
@ -31,6 +33,43 @@ public abstract class AbstractRouterRule {
private String scope;
private String key;
protected void parseFromMap0(Map<String, Object> map) {
setRawRule((String) map.get("rawRule"));
Object runtime = map.get("runtime");
if (runtime != null) {
setRuntime(Boolean.parseBoolean(runtime.toString()));
}
Object force = map.get("force");
if (force != null) {
setForce(Boolean.parseBoolean(force.toString()));
}
Object valid = map.get("valid");
if (valid != null) {
setValid(Boolean.parseBoolean(valid.toString()));
}
Object enabled = map.get("enabled");
if (enabled != null) {
setEnabled(Boolean.parseBoolean(enabled.toString()));
}
Object priority = map.get("priority");
if (priority != null) {
setPriority(Integer.parseInt(priority.toString()));
}
Object dynamic = map.get("dynamic");
if (dynamic != null) {
setDynamic(Boolean.parseBoolean(dynamic.toString()));
}
setScope((String) map.get("scope"));
setKey((String) map.get("key"));
}
public String getRawRule() {
return rawRule;
}

View File

@ -19,15 +19,31 @@ package org.apache.dubbo.rpc.cluster.router.condition.config.model;
import org.apache.dubbo.rpc.cluster.router.AbstractRouterRule;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
/**
*
*/
public class ConditionRouterRule extends AbstractRouterRule {
public ConditionRouterRule() {
private List<String> conditions;
@SuppressWarnings("unchecked")
public static ConditionRouterRule parseFromMap(Map<String, Object> map) {
ConditionRouterRule conditionRouterRule = new ConditionRouterRule();
conditionRouterRule.parseFromMap0(map);
Object conditions = map.get("conditions");
if (conditions != null && List.class.isAssignableFrom(conditions.getClass())) {
conditionRouterRule.setConditions(((List<Object>) conditions).stream()
.map(String::valueOf).collect(Collectors.toList()));
}
return conditionRouterRule;
}
private List<String> conditions;
public ConditionRouterRule() {
}
public List<String> getConditions() {
return conditions;

View File

@ -19,7 +19,9 @@ package org.apache.dubbo.rpc.cluster.router.condition.config.model;
import org.apache.dubbo.common.utils.CollectionUtils;
import org.yaml.snakeyaml.Yaml;
import org.yaml.snakeyaml.constructor.Constructor;
import org.yaml.snakeyaml.constructor.SafeConstructor;
import java.util.Map;
/**
* %YAML1.2
@ -38,10 +40,9 @@ import org.yaml.snakeyaml.constructor.Constructor;
public class ConditionRuleParser {
public static ConditionRouterRule parse(String rawRule) {
Constructor constructor = new Constructor(ConditionRouterRule.class);
Yaml yaml = new Yaml(constructor);
ConditionRouterRule rule = yaml.load(rawRule);
Yaml yaml = new Yaml(new SafeConstructor());
Map<String, Object> map = yaml.load(rawRule);
ConditionRouterRule rule = ConditionRouterRule.parseFromMap(map);
rule.setRawRule(rawRule);
if (CollectionUtils.isEmpty(rule.getConditions())) {
rule.setValid(false);

View File

@ -32,6 +32,13 @@ import javax.script.CompiledScript;
import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
import javax.script.ScriptException;
import java.security.AccessControlContext;
import java.security.AccessController;
import java.security.CodeSource;
import java.security.Permissions;
import java.security.PrivilegedAction;
import java.security.ProtectionDomain;
import java.security.cert.Certificate;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
@ -62,6 +69,17 @@ public class ScriptRouter extends AbstractRouter {
private CompiledScript function;
private AccessControlContext accessControlContext;
{
//Just give permission of reflect to access member.
Permissions perms = new Permissions();
perms.add(new RuntimePermission("accessDeclaredMembers"));
// Cast to Certificate[] required because of ambiguity:
ProtectionDomain domain = new ProtectionDomain(new CodeSource(null, (Certificate[]) null), perms);
accessControlContext = new AccessControlContext(new ProtectionDomain[]{domain});
}
public ScriptRouter(URL url) {
this.url = url;
this.priority = url.getParameter(PRIORITY_KEY, SCRIPT_ROUTER_DEFAULT_PRIORITY);
@ -75,8 +93,6 @@ public class ScriptRouter extends AbstractRouter {
logger.error("route error, rule has been ignored. rule: " + rule +
", url: " + RpcContext.getServiceContext().getUrl(), e);
}
}
/**
@ -107,17 +123,19 @@ public class ScriptRouter extends AbstractRouter {
@Override
public <T> List<Invoker<T>> route(List<Invoker<T>> invokers, URL url, Invocation invocation) throws RpcException {
try {
Bindings bindings = createBindings(invokers, invocation);
if (function == null) {
return invokers;
}
return getRoutedInvokers(function.eval(bindings));
} catch (ScriptException e) {
logger.error("route error, rule has been ignored. rule: " + rule + ", method:" +
invocation.getMethodName() + ", url: " + RpcContext.getServiceContext().getUrl(), e);
if (engine == null || function == null) {
return invokers;
}
Bindings bindings = createBindings(invokers, invocation);
return getRoutedInvokers(AccessController.doPrivileged((PrivilegedAction<Object>) () -> {
try {
return function.eval(bindings);
} catch (ScriptException e) {
logger.error("route error, rule has been ignored. rule: " + rule + ", method:" +
invocation.getMethodName() + ", url: " + RpcContext.getContext().getUrl(), e);
return invokers;
}
}, accessControlContext));
}
/**

View File

@ -17,6 +17,8 @@
package org.apache.dubbo.rpc.cluster.router.tag.model;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
/**
*
@ -25,6 +27,19 @@ public class Tag {
private String name;
private List<String> addresses;
@SuppressWarnings("unchecked")
public static Tag parseFromMap(Map<String, Object> map) {
Tag tag = new Tag();
tag.setName((String) map.get("name"));
Object addresses = map.get("addresses");
if (addresses != null && List.class.isAssignableFrom(addresses.getClass())) {
tag.setAddresses(((List<Object>) addresses).stream().map(String::valueOf).collect(Collectors.toList()));
}
return tag;
}
public String getName() {
return name;
}

View File

@ -46,6 +46,20 @@ public class TagRouterRule extends AbstractRouterRule {
private Map<String, List<String>> addressToTagnames = new HashMap<>();
private Map<String, List<String>> tagnameToAddresses = new HashMap<>();
@SuppressWarnings("unchecked")
public static TagRouterRule parseFromMap(Map<String, Object> map) {
TagRouterRule tagRouterRule = new TagRouterRule();
tagRouterRule.parseFromMap0(map);
Object tags = map.get("tags");
if (tags != null && List.class.isAssignableFrom(tags.getClass())) {
tagRouterRule.setTags(((List<Map<String, Object>>) tags).stream()
.map(Tag::parseFromMap).collect(Collectors.toList()));
}
return tagRouterRule;
}
public void init() {
if (!isValid()) {
return;

View File

@ -17,9 +17,11 @@
package org.apache.dubbo.rpc.cluster.router.tag.model;
import org.apache.dubbo.common.utils.CollectionUtils;
import org.yaml.snakeyaml.TypeDescription;
import org.yaml.snakeyaml.Yaml;
import org.yaml.snakeyaml.constructor.Constructor;
import org.yaml.snakeyaml.constructor.SafeConstructor;
import java.util.Map;
/**
*
@ -27,13 +29,9 @@ import org.yaml.snakeyaml.constructor.Constructor;
public class TagRuleParser {
public static TagRouterRule parse(String rawRule) {
Constructor constructor = new Constructor(TagRouterRule.class);
TypeDescription tagDescription = new TypeDescription(TagRouterRule.class);
tagDescription.addPropertyParameters("tags", Tag.class);
constructor.addTypeDescription(tagDescription);
Yaml yaml = new Yaml(constructor);
TagRouterRule rule = yaml.load(rawRule);
Yaml yaml = new Yaml(new SafeConstructor());
Map<String, Object> map = yaml.load(rawRule);
TagRouterRule rule = TagRouterRule.parseFromMap(map);
rule.setRawRule(rawRule);
if (CollectionUtils.isEmpty(rule.getTags())) {
rule.setValid(false);

View File

@ -1,5 +1,4 @@
file=org.apache.dubbo.rpc.cluster.router.file.FileRouterFactory
script=org.apache.dubbo.rpc.cluster.router.script.ScriptRouterFactory
condition=org.apache.dubbo.rpc.cluster.router.condition.ConditionRouterFactory
service=org.apache.dubbo.rpc.cluster.router.condition.config.ServiceRouterFactory
app=org.apache.dubbo.rpc.cluster.router.condition.config.AppRouterFactory

View File

@ -17,18 +17,17 @@
package org.apache.dubbo.rpc.cluster.configurator.parser;
import org.apache.dubbo.common.URL;
import org.apache.dubbo.rpc.cluster.configurator.parser.model.ConfigItem;
import org.apache.dubbo.rpc.cluster.configurator.parser.model.ConfiguratorConfig;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import org.yaml.snakeyaml.TypeDescription;
import org.yaml.snakeyaml.Yaml;
import org.yaml.snakeyaml.constructor.Constructor;
import org.yaml.snakeyaml.constructor.SafeConstructor;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;
import java.util.Map;
import static org.apache.dubbo.common.constants.CommonConstants.LOADBALANCE_KEY;
import static org.apache.dubbo.common.constants.CommonConstants.TIMEOUT_KEY;
@ -49,14 +48,9 @@ public class ConfigParserTest {
@Test
public void snakeYamlBasicTest() throws IOException {
try (InputStream yamlStream = this.getClass().getResourceAsStream("/ServiceNoApp.yml")) {
Constructor constructor = new Constructor(ConfiguratorConfig.class);
TypeDescription carDescription = new TypeDescription(ConfiguratorConfig.class);
carDescription.addPropertyParameters("items", ConfigItem.class);
constructor.addTypeDescription(carDescription);
Yaml yaml = new Yaml(constructor);
ConfiguratorConfig config = yaml.load(yamlStream);
Yaml yaml = new Yaml(new SafeConstructor());
Map<String, Object> map = yaml.load(yamlStream);
ConfiguratorConfig config = ConfiguratorConfig.parseFromMap(map);
System.out.println(config);
}
}

View File

@ -97,6 +97,8 @@ public class TagRouterTest {
TagRouterRule tagRouterRule = TagRuleParser.parse(tagRouterRuleConfig);
// assert tags
assert tagRouterRule.getKey().equals("demo-provider");
assert tagRouterRule.getPriority() == 1;
assert tagRouterRule.getTagNames().contains("tag1");
assert tagRouterRule.getTagNames().contains("tag2");
assert tagRouterRule.getTagNames().contains("tag3");

View File

@ -0,0 +1 @@
script=org.apache.dubbo.rpc.cluster.router.script.ScriptRouterFactory

View File

@ -109,6 +109,12 @@ public final class URLStrParser {
*/
private static URL parseURLBody(String fullURLStr, String decodedBody, Map<String, String> parameters, boolean modifiable) {
int starIdx = 0, endIdx = decodedBody.length();
// ignore the url content following '#'
int poundIndex = decodedBody.indexOf('#');
if (poundIndex != -1) {
endIdx = poundIndex;
}
String protocol = null;
int protoEndIdx = decodedBody.indexOf("://");
if (protoEndIdx >= 0) {
@ -132,7 +138,7 @@ public final class URLStrParser {
String path = null;
int pathStartIdx = indexOf(decodedBody, '/', starIdx, endIdx);
if (pathStartIdx >= 0) {
path = decodedBody.substring(pathStartIdx + 1);
path = decodedBody.substring(pathStartIdx + 1, endIdx);
endIdx = pathStartIdx;
}

View File

@ -20,6 +20,7 @@ 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;
@ -464,6 +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);
}

View File

@ -377,6 +377,16 @@ public interface CommonConstants {
*/
String DEFAULT_SERVICE_NAME_MAPPING_PROPERTIES_PATH = "META-INF/dubbo/service-name-mapping.properties";
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";
String QOS_LIVE_PROBE_EXTENSION = "dubbo.application.liveness-probe";
String QOS_READY_PROBE_EXTENSION = "dubbo.application.readiness-probe";

View File

@ -386,6 +386,7 @@ 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);
try {
type = ClassUtils.forName((String) className);
} catch (ClassNotFoundException e) {

View File

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

View File

@ -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

View File

@ -38,6 +38,22 @@ import static org.junit.jupiter.api.Assertions.fail;
public class URLTest {
@Test
public void test_ignore_pond() {
URL url = URL.valueOf("dubbo://admin:hello1234@10.20.130.230:20880/context/path#index?version=1.0.0&id=org.apache.dubbo.config.RegistryConfig#0");
assertURLStrDecoder(url);
assertEquals("dubbo", url.getProtocol());
assertEquals("admin", url.getUsername());
assertEquals("hello1234", url.getPassword());
assertEquals("10.20.130.230", url.getHost());
assertEquals("10.20.130.230:20880", url.getAddress());
assertEquals(20880, url.getPort());
assertEquals("context/path", url.getPath());
assertEquals(2, url.getParameters().size());
assertEquals("1.0.0", url.getVersion());
assertEquals("org.apache.dubbo.config.RegistryConfig#0", url.getParameter("id"));
}
@Test
public void test_valueOf_noProtocolAndHost() throws Exception {
URL url = URL.valueOf("/context/path?version=1.0.0&application=morgan");

View File

@ -0,0 +1,105 @@
/*
* 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.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;
public class SerializeClassCheckerTest {
@BeforeEach
public void setUp() {
SerializeClassChecker.clearInstance();
}
@Test
public 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
public 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
public 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
public 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

@ -16,11 +16,30 @@
*/
package org.apache.dubbo.registry.client.migration.model;
import java.util.Map;
public class ApplicationMigrationRule {
private String name;
private MigrationStep step;
private Float threshold;
public static ApplicationMigrationRule parseFromMap(Map<String, Object> map) {
ApplicationMigrationRule applicationMigrationRule = new ApplicationMigrationRule();
applicationMigrationRule.setName((String) map.get("name"));
Object step = map.get("step");
if (step != null) {
applicationMigrationRule.setStep(MigrationStep.valueOf(step.toString()));
}
Object threshold = map.get("threshold");
if (threshold != null) {
applicationMigrationRule.setThreshold(Float.valueOf(threshold.toString()));
}
return applicationMigrationRule;
}
public ApplicationMigrationRule() {
}

View File

@ -16,12 +16,32 @@
*/
package org.apache.dubbo.registry.client.migration.model;
import java.util.Map;
public class InterfaceMigrationRule {
private String appName;
private String serviceKey;
private MigrationStep step;
private Float threshold;
public static InterfaceMigrationRule parseFromMap(Map<String, Object> map) {
InterfaceMigrationRule interfaceMigrationRule = new InterfaceMigrationRule();
interfaceMigrationRule.setAppName((String) map.get("appName"));
interfaceMigrationRule.setServiceKey((String) map.get("serviceKey"));
Object step = map.get("step");
if (step != null) {
interfaceMigrationRule.setStep(MigrationStep.valueOf(step.toString()));
}
Object threshold = map.get("threshold");
if (threshold != null) {
interfaceMigrationRule.setThreshold(Float.valueOf(threshold.toString()));
}
return interfaceMigrationRule;
}
public InterfaceMigrationRule(){}
public InterfaceMigrationRule(String appName, String serviceKey, MigrationStep step, Float threshold) {

View File

@ -20,12 +20,14 @@ import org.apache.dubbo.common.utils.CollectionUtils;
import org.yaml.snakeyaml.Yaml;
import org.yaml.snakeyaml.constructor.Constructor;
import org.yaml.snakeyaml.constructor.SafeConstructor;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
/**
* # key = demo-consumer.migration
@ -55,6 +57,42 @@ public class MigrationRule {
private transient Map<String, InterfaceMigrationRule> interfaceRules;
private transient Map<String, ApplicationMigrationRule> applicationRules;
@SuppressWarnings("unchecked")
private static MigrationRule parseFromMap(Map<String, Object> map) {
MigrationRule migrationRule = new MigrationRule();
migrationRule.setKey((String) map.get("key"));
Object step = map.get("step");
if (step != null) {
migrationRule.setStep(MigrationStep.valueOf(step.toString()));
}
Object threshold = map.get("threshold");
if (threshold != null) {
migrationRule.setThreshold(Float.valueOf(threshold.toString()));
}
Object targetIps = map.get("targetIps");
if (targetIps != null && List.class.isAssignableFrom(targetIps.getClass())) {
migrationRule.setTargetIps(((List<Object>) targetIps).stream()
.map(String::valueOf).collect(Collectors.toList()));
}
Object interfaces = map.get("interfaces");
if (interfaces != null && List.class.isAssignableFrom(interfaces.getClass())) {
migrationRule.setInterfaces(((List<Map<String, Object>>) interfaces).stream()
.map(InterfaceMigrationRule::parseFromMap).collect(Collectors.toList()));
}
Object applications = map.get("applications");
if (applications != null && List.class.isAssignableFrom(applications.getClass())) {
migrationRule.setApplications(((List<Map<String, Object>>) applications).stream()
.map(ApplicationMigrationRule::parseFromMap).collect(Collectors.toList()));
}
return migrationRule;
}
public MigrationRule() {
}
@ -233,9 +271,9 @@ public class MigrationRule {
}
public static MigrationRule parse(String rawRule) {
Constructor constructor = new Constructor(MigrationRule.class);
Yaml yaml = new Yaml(constructor);
return yaml.load(rawRule);
Yaml yaml = new Yaml(new SafeConstructor());
Map<String, Object> map = yaml.load(rawRule);
return parseFromMap(map);
}
public static String toYaml(MigrationRule rule) {

View File

@ -0,0 +1,48 @@
/*
* 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.registry.client.migration.model;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertNull;
public class MigrationRuleTest {
@Test
public void test_parse() {
String rule = "key: demo-consumer\n" +
"step: APPLICATION_FIRST\n" +
"threshold: 1.0\n" +
"interfaces:\n" +
" - serviceKey: DemoService:1.0.0\n" +
" threshold: 1.0\n" +
" step: APPLICATION_FIRST\n" +
" - serviceKey: GreetingService:1.0.0\n" +
" step: FORCE_APPLICATION";
MigrationRule migrationRule = MigrationRule.parse(rule);
assertEquals(migrationRule.getKey(), "demo-consumer");
assertEquals(migrationRule.getStep(), MigrationStep.APPLICATION_FIRST);
assertEquals(migrationRule.getThreshold(), 1.0f);
assertEquals(migrationRule.getInterfaces().size(), 2);
assertNotNull(migrationRule.getInterfaceRule("DemoService:1.0.0"));
assertNotNull(migrationRule.getInterfaceRule("GreetingService:1.0.0"));
assertNull(migrationRule.getApplications());
}
}

View File

@ -19,11 +19,14 @@ package org.apache.dubbo.rpc.filter;
import org.apache.dubbo.common.beanutil.JavaBeanAccessor;
import org.apache.dubbo.common.beanutil.JavaBeanDescriptor;
import org.apache.dubbo.common.beanutil.JavaBeanSerializeUtil;
import org.apache.dubbo.common.config.Configuration;
import org.apache.dubbo.common.constants.CommonConstants;
import org.apache.dubbo.common.extension.Activate;
import org.apache.dubbo.common.extension.ExtensionLoader;
import org.apache.dubbo.common.io.UnsafeByteArrayInputStream;
import org.apache.dubbo.common.io.UnsafeByteArrayOutputStream;
import org.apache.dubbo.common.logger.Logger;
import org.apache.dubbo.common.logger.LoggerFactory;
import org.apache.dubbo.common.serialize.Serialization;
import org.apache.dubbo.common.utils.PojoUtils;
import org.apache.dubbo.common.utils.ReflectUtils;
@ -35,6 +38,7 @@ import org.apache.dubbo.rpc.Result;
import org.apache.dubbo.rpc.RpcContext;
import org.apache.dubbo.rpc.RpcException;
import org.apache.dubbo.rpc.RpcInvocation;
import org.apache.dubbo.rpc.model.ApplicationModel;
import org.apache.dubbo.rpc.service.GenericException;
import org.apache.dubbo.rpc.service.GenericService;
import org.apache.dubbo.rpc.support.ProtocolUtils;
@ -54,6 +58,7 @@ import static org.apache.dubbo.rpc.Constants.GENERIC_KEY;
*/
@Activate(group = CommonConstants.PROVIDER, order = -20000)
public class GenericFilter implements Filter, Filter.Listener {
private final Logger logger = LoggerFactory.getLogger(GenericFilter.class);
@Override
public Result invoke(Invoker<?> invoker, Invocation inv) throws RpcException {
@ -85,6 +90,18 @@ public class GenericFilter implements Filter, Filter.Listener {
|| ProtocolUtils.isGenericReturnRawResult(generic)) {
args = PojoUtils.realize(args, params, method.getGenericParameterTypes());
} else if (ProtocolUtils.isJavaGenericSerialization(generic)) {
Configuration configuration = ApplicationModel.getEnvironment().getConfiguration();
if (!configuration.getBoolean(CommonConstants.ENABLE_NATIVE_JAVA_GENERIC_SERIALIZE, false)) {
String notice = "Trigger the safety barrier! " +
"Native Java Serializer is not allowed by default." +
"This means currently maybe being attacking by others. " +
"If you are sure this is a mistake, " +
"please set `" + CommonConstants.ENABLE_NATIVE_JAVA_GENERIC_SERIALIZE + "` enable in configuration! " +
"Before doing so, please make sure you have configure JEP290 to prevent serialization attack.";
logger.error(notice);
throw new RpcException(new IllegalStateException(notice));
}
for (int i = 0; i < args.length; i++) {
if (byte[].class == args[i].getClass()) {
try (UnsafeByteArrayInputStream is = new UnsafeByteArrayInputStream((byte[]) args[i])) {

View File

@ -37,6 +37,7 @@ import java.util.HashMap;
import java.util.Map;
import static org.apache.dubbo.common.constants.CommonConstants.$INVOKE;
import static org.apache.dubbo.common.constants.CommonConstants.ENABLE_NATIVE_JAVA_GENERIC_SERIALIZE;
import static org.apache.dubbo.common.constants.CommonConstants.GENERIC_SERIALIZATION_NATIVE_JAVA;
import static org.apache.dubbo.rpc.Constants.GENERIC_KEY;
import static org.mockito.ArgumentMatchers.any;
@ -75,6 +76,8 @@ public class GenericFilterTest {
@Test
public void testInvokeWithJavaException() throws Exception {
// temporary enable native java generic serialize
System.setProperty(ENABLE_NATIVE_JAVA_GENERIC_SERIALIZE, "true");
Assertions.assertThrows(RpcException.class, () -> {
Method genericInvoke = GenericService.class.getMethods()[0];
@ -95,6 +98,7 @@ public class GenericFilterTest {
genericFilter.invoke(invoker, invocation);
});
System.clearProperty(ENABLE_NATIVE_JAVA_GENERIC_SERIALIZE);
}
@Test