Skip to content

Commit

Permalink
backport changes from 2.7(#5767)
Browse files Browse the repository at this point in the history
  • Loading branch information
chickenlj committed Feb 20, 2020
1 parent a8f6f99 commit 112990d
Show file tree
Hide file tree
Showing 12 changed files with 466 additions and 6 deletions.
1 change: 1 addition & 0 deletions distribution/src/assembly/source-release.xml
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@
<exclude>**/.mvn/**</exclude>
<exclude>**/*.jar</exclude>
<exclude>**/mvnw*</exclude>
<exclude>**/invalid_data.ser</exclude>
</excludes>
</fileSet>
</fileSets>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,209 @@
/*
* 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.alibaba.dubbo.common.utils;

import com.alibaba.dubbo.common.Constants;
import com.alibaba.dubbo.common.logger.Logger;
import com.alibaba.dubbo.common.logger.LoggerFactory;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.InvalidClassException;
import java.io.ObjectInputStream;
import java.io.ObjectStreamClass;
import java.util.Arrays;
import java.util.Iterator;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class SerialDetector extends ObjectInputStream {

private static final Logger logger = LoggerFactory.getLogger(SerialDetector.class);

private static final BlacklistConfiguration configuration = new BlacklistConfiguration();

/**
* Wrapper Constructor.
*
* @param inputStream The original InputStream, used by your service to receive serialized objects
* @throws java.io.IOException File I/O exception
* @throws IllegalStateException Invalid configuration exception
*/
public SerialDetector(final InputStream inputStream) throws IOException {
super(inputStream);
}

@Override
protected Class<?> resolveClass(final ObjectStreamClass serialInput) throws IOException, ClassNotFoundException {
if (isClassInBlacklist(serialInput)) {
if (!configuration.shouldCheck()) {
// Reporting mode
logger.info(String.format("Blacklist match: '%s'", serialInput.getName()));
} else {
// Blocking mode
logger.error(String.format("Blocked by blacklist'. Match found for '%s'", serialInput.getName()));
throw new InvalidClassException(serialInput.getName(), "Class blocked from deserialization (blacklist)");
}
}

return super.resolveClass(serialInput);
}

public static boolean isClassInBlacklist(final ObjectStreamClass serialInput) {
// Enforce SerialDetector's blacklist
Iterable<Pattern> blacklistIterable = configuration.blacklist();
if (blacklistIterable == null) {
return false;
}

boolean inBlacklist = false;
for (Pattern blackPattern : blacklistIterable) {
Matcher blackMatcher = blackPattern.matcher(serialInput.getName());
if (blackMatcher.find()) {
inBlacklist = true;
break;
}
}
return inBlacklist;
}

public static boolean shouldCheck() {
return configuration.shouldCheck();
}

static final class BlacklistConfiguration {
private static final String DUBBO_SECURITY_SERIALIZATION_CHECK = "dubbo.security.serialization.check";
private static final String DUBBO_SECURITY_SERIALIZATION_BLACKLIST = "dubbo.security.serialization.blacklist";
private static final String DUBBO_SECURITY_SERIALIZATION_BLACKLIST_FILE = "dubbo.registry.serialization.blacklist.file";

private boolean check;
private PatternList blacklistPattern;

BlacklistConfiguration() {
try {
check = Boolean.parseBoolean(getSecurityProperty(DUBBO_SECURITY_SERIALIZATION_CHECK, "false"));
String blacklist = getSecurityProperty(DUBBO_SECURITY_SERIALIZATION_BLACKLIST, "");
if (StringUtils.isEmpty(blacklist)) {
String blacklistFile = getSecurityProperty(DUBBO_SECURITY_SERIALIZATION_BLACKLIST_FILE, "");
if (StringUtils.isNotEmpty(blacklistFile)) {
blacklist = loadBlacklistFile(blacklistFile);
}
}
blacklistPattern = new PatternList(Constants.COMMA_SPLIT_PATTERN.split(blacklist));
} catch (Throwable t) {
logger.warn("Failed to initialize the Serialization Security Checker component!", t);
}
}

Iterable<Pattern> blacklist() {
return blacklistPattern;
}

boolean shouldCheck() {
return check;
}

private String loadBlacklistFile(String fileName) {
StringBuilder blacklist = new StringBuilder();
InputStream is = null;

File file = new File(fileName);
if (file.exists()) {
try {
is = new FileInputStream(fileName);
} catch (Throwable e) {
logger.warn("Failed to load " + fileName + " file " + e.getMessage(), e);
}
} else {
is = this.getClass().getClassLoader().getResourceAsStream(fileName);
}

try {
BufferedReader reader = new BufferedReader(new InputStreamReader(is));
String line = null;
while ((line = reader.readLine()) != null) {
if (!line.startsWith("#") && StringUtils.isNotEmpty(line)) {
blacklist.append(line);
blacklist.append(",");
}
}
} catch (Throwable e) {
logger.warn("Failed to read from file " + fileName + e.getMessage(), e);
}
return blacklist.toString();
}

private String getSecurityProperty(String key, String defaultValue) {
String value = ConfigUtils.getSystemProperty(key);
if (StringUtils.isEmpty(value)) {
value = ConfigUtils.getProperty(key);
}
return StringUtils.isEmpty(value) ? defaultValue : value;
}

}

static final class PatternList implements Iterable<Pattern> {
private final Pattern[] patterns;

PatternList(final String... regExps) {

for (int i = 0; i < regExps.length; i++) {
if (regExps[i] == null) {
throw new IllegalStateException("Deserialization blacklist reg expression cannot be null!");
}
}

this.patterns = new Pattern[regExps.length];
for (int i = 0; i < regExps.length; i++) {
patterns[i] = Pattern.compile(regExps[i]);
}
}

@Override
public Iterator<Pattern> iterator() {
return new Iterator<Pattern>() {
int index = 0;

@Override
public boolean hasNext() {
return index < patterns.length;
}

@Override
public Pattern next() {
return patterns[index++];
}

@Override
public void remove() {
throw new UnsupportedOperationException("remove");
}
};
}

@Override
public String toString() {
return Arrays.toString(patterns);
}

}
}
101 changes: 101 additions & 0 deletions dubbo-common/src/main/resources/serial-blacklist.properties
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
#
# 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.
#
bsh.XThis
bsh.Interpreter
com.mchange.v2.c3p0.PoolBackedDataSource
com.mchange.v2.c3p0.impl.PoolBackedDataSourceBase
clojure.lang.PersistentArrayMap
clojure.inspector.proxy$javax.swing.table.AbstractTableModel$ff19274a
org.apache.commons.beanutils.BeanComparator
org.apache.commons.collections.Transformer
org.apache.commons.collections.functors.ChainedTransformer
org.apache.commons.collections.functors.ConstantTransformer
org.apache.commons.collections.functors.InstantiateTransformer
org.apache.commons.collections.map.LazyMap
org.apache.commons.collections.functors.InvokerTransformer
org.apache.commons.collections.keyvalue.TiedMapEntry
org.apache.commons.collections4.comparators.TransformingComparator
org.apache.commons.collections4.functors.InvokerTransformer
org.apache.commons.collections4.functors.ChainedTransformer
org.apache.commons.collections4.functors.ConstantTransformer
org.apache.commons.collections4.functors.InstantiateTransformer
org.apache.commons.fileupload.disk.DiskFileItem
org.apache.commons.io.output.DeferredFileOutputStream
org.apache.commons.io.output.ThresholdingOutputStream
org.apache.wicket.util.upload.DiskFileItem
org.apache.wicket.util.io.DeferredFileOutputStream
org.apache.wicket.util.io.ThresholdingOutputStream
org.codehaus.groovy.runtime.ConvertedClosure
org.codehaus.groovy.runtime.MethodClosure
org.hibernate.engine.spi.TypedValue
org.hibernate.tuple.component.AbstractComponentTuplizer
org.hibernate.tuple.component.PojoComponentTuplizer
org.hibernate.type.AbstractType
org.hibernate.type.ComponentType
org.hibernate.type.Type
org.hibernate.EntityMode
com.sun.rowset.JdbcRowSetImpl
org.jboss.interceptor.builder.InterceptionModelBuilder
org.jboss.interceptor.builder.MethodReference
org.jboss.interceptor.proxy.DefaultInvocationContextFactory
org.jboss.interceptor.proxy.InterceptorMethodHandler
org.jboss.interceptor.reader.ClassMetadataInterceptorReference
org.jboss.interceptor.reader.DefaultMethodMetadata
org.jboss.interceptor.reader.ReflectiveClassMetadata
org.jboss.interceptor.reader.SimpleInterceptorMetadata
org.jboss.interceptor.spi.instance.InterceptorInstantiator
org.jboss.interceptor.spi.metadata.InterceptorReference
org.jboss.interceptor.spi.metadata.MethodMetadata
org.jboss.interceptor.spi.model.InterceptionType
org.jboss.interceptor.spi.model.InterceptionModel
sun.rmi.server.UnicastRef
sun.rmi.transport.LiveRef
sun.rmi.transport.tcp.TCPEndpoint
java.rmi.server.RemoteObject
java.rmi.server.RemoteRef
java.rmi.server.UnicastRemoteObject
sun.rmi.server.ActivationGroupImpl
sun.rmi.server.UnicastServerRef
net.sf.json.JSONObject
org.jboss.weld.interceptor.builder.InterceptionModelBuilder
org.jboss.weld.interceptor.builder.MethodReference
org.jboss.weld.interceptor.proxy.DefaultInvocationContextFactory
org.jboss.weld.interceptor.proxy.InterceptorMethodHandler
org.jboss.weld.interceptor.reader.ClassMetadataInterceptorReference
org.jboss.weld.interceptor.reader.DefaultMethodMetadata
org.jboss.weld.interceptor.reader.ReflectiveClassMetadata
org.jboss.weld.interceptor.reader.SimpleInterceptorMetadata
org.jboss.weld.interceptor.spi.instance.InterceptorInstantiator
org.jboss.weld.interceptor.spi.metadata.InterceptorReference
org.jboss.weld.interceptor.spi.metadata.MethodMetadata
org.jboss.weld.interceptor.spi.model.InterceptionModel
org.jboss.weld.interceptor.spi.model.InterceptionType
org.python.core.PyObject
org.python.core.PyBytecode
org.python.core.PyFunction
org.mozilla.javascript.**
org.apache.myfaces.context.servlet.FacesContextImpl
org.apache.myfaces.context.servlet.FacesContextImplBase
org.apache.myfaces.el.CompositeELResolver
org.apache.myfaces.el.unified.FacesELContext
org.apache.myfaces.view.facelets.el.ValueExpressionMethodExpression
com.sun.syndication.feed.impl.ObjectBean
org.springframework.beans.factory.ObjectFactory
org.springframework.aop.framework.AdvisedSupport
org.springframework.aop.target.SingletonTargetSource
com.vaadin.data.util.NestedMethodProperty
com.vaadin.data.util.PropertysetItem;
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
/*
* 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.alibaba.dubbo.common.utils;

import org.junit.Test;

import java.io.InvalidClassException;
import java.io.ObjectInputStream;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;

public class SerialDetectorTest {
@Test
public void testBlacklisted() throws Exception {
try {
ObjectInputStream stream = new SerialDetector(getClass().getResourceAsStream("/security/invalid_data.ser"));
stream.readObject();
fail();
} catch (InvalidClassException expected) {
assertTrue(expected.getMessage().contains("blocked"));
assertTrue(expected.getMessage().contains("blacklist"));
assertEquals(expected.classname, "org.hibernate.engine.spi.TypedValue");
} catch (ClassNotFoundException e) {
fail(e.getMessage());
}
}


}
4 changes: 3 additions & 1 deletion dubbo-common/src/test/resources/dubbo.properties
Original file line number Diff line number Diff line change
Expand Up @@ -15,4 +15,6 @@
# limitations under the License.
#

dubbo=properties
dubbo=properties
dubbo.security.serialization.blacklist=org.hibernate.engine.spi.TypedValue
dubbo.security.serialization.check=true
Binary file not shown.

0 comments on commit 112990d

Please sign in to comment.