Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add basic protection against untrusted deserialization by introducing blacklisting/whitelisting capabilities #12230

Merged
merged 1 commit into from May 15, 2018
Merged
Changes from all commits
Commits
File filter...
Filter file types
Jump to…
Jump to file or symbol
Failed to load files and symbols.

Always

Just for now

Add basic protection against untrusted deserialization.

  • Loading branch information...
kwart committed Apr 26, 2016
commit c1c31359a1df953e79a9ca9b6c54b3cdbfef11e9
@@ -526,6 +526,13 @@
</xs:complexType>
</xs:element>
<xs:element name="check-class-def-errors" type="xs:boolean" minOccurs="0" maxOccurs="1" default="true"/>
<xs:element name="java-serialization-filter" type="java-serialization-filter" minOccurs="0" maxOccurs="1">
<xs:annotation>
<xs:documentation>
Basic protection against untrusted deserialization based on class/package blacklisting and whitelisting.
</xs:documentation>
</xs:annotation>
</xs:element>
</xs:all>
</xs:complexType>
<xs:complexType name="serialization-factory">
@@ -553,6 +560,44 @@
</xs:extension>
</xs:simpleContent>
</xs:complexType>
<xs:complexType name="java-serialization-filter">
<xs:all>
<xs:element name="blacklist" type="filter-list" minOccurs="0" maxOccurs="1">
<xs:annotation>
<xs:documentation>
Blacklisted classes and packages, which are not allowed to be deserialized.
</xs:documentation>
</xs:annotation>
</xs:element>
<xs:element name="whitelist" type="filter-list" minOccurs="0" maxOccurs="1">
<xs:annotation>
<xs:documentation>
Whitelisted classes and packages, which are allowed to be deserialized. If the list is empty
(no class or package name provided) then all classes are allowed.
</xs:documentation>
</xs:annotation>
</xs:element>
</xs:all>
</xs:complexType>
<xs:complexType name="filter-list">
<xs:choice minOccurs="0" maxOccurs="unbounded">
<xs:element name="class" type="xs:string" minOccurs="0" maxOccurs="unbounded">
<xs:annotation>
<xs:documentation>
Name of a class to be included in the list.
</xs:documentation>
</xs:annotation>
</xs:element>
<xs:element name="package" type="xs:string" minOccurs="0" maxOccurs="unbounded">
<xs:annotation>
<xs:documentation>
Name of a package to be included in the list.
</xs:documentation>
</xs:annotation>
</xs:element>
</xs:choice>
</xs:complexType>

<xs:complexType name="socket-interceptor">
<xs:all>
<xs:element name="class-name" type="xs:string" minOccurs="0" maxOccurs="1"/>
@@ -134,6 +134,17 @@
class-name="com.hazelcast.examples.SerializerFactory"/>
</serializers>
<check-class-def-errors>true</check-class-def-errors>
<java-serialization-filter>
<blacklist>
<class>com.acme.app.BeanComparator</class>
</blacklist>
<whitelist>
<class>java.lang.String</class>
<class>example.Foo</class>
<package>com.acme.app</package>
<package>com.acme.app.subpkg</package>
</whitelist>
</java-serialization-filter>
</serialization>

<native-memory enabled="false" allocator-type="POOLED">
@@ -17,12 +17,14 @@
package com.hazelcast.spring;

import com.hazelcast.config.AbstractXmlConfigHelper;
import com.hazelcast.config.ClassFilter;
import com.hazelcast.config.DiscoveryConfig;
import com.hazelcast.config.DiscoveryStrategyConfig;
import com.hazelcast.config.EvictionConfig;
import com.hazelcast.config.EvictionPolicy;
import com.hazelcast.config.GlobalSerializerConfig;
import com.hazelcast.config.InvalidConfigurationException;
import com.hazelcast.config.JavaSerializationFilterConfig;
import com.hazelcast.config.NearCachePreloaderConfig;
import com.hazelcast.config.SerializationConfig;
import com.hazelcast.config.SerializerConfig;
@@ -35,6 +37,7 @@
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.ManagedList;
import org.springframework.beans.factory.support.ManagedMap;
import org.springframework.beans.factory.support.ManagedSet;
import org.springframework.beans.factory.xml.AbstractBeanDefinitionParser;
import org.springframework.beans.factory.xml.ParserContext;
import org.w3c.dom.NamedNodeMap;
@@ -306,6 +309,8 @@ protected void handleSerialization(Node node) {
handlePortableFactories(child, serializationConfigBuilder);
} else if ("serializers".equals(nodeName)) {
handleSerializers(child, serializationConfigBuilder);
} else if ("java-serialization-filter".equals(nodeName)) {
handleJavaSerializationFilter(child, serializationConfigBuilder);
}
}
configBuilder.addPropertyValue("serializationConfig", beanDefinition);
@@ -511,5 +516,36 @@ private void handleDiscoveryStrategy(Node node, ManagedList<BeanDefinition> disc
}
discoveryStrategyConfigs.add(discoveryStrategyConfigBuilder.getBeanDefinition());
}

protected void handleJavaSerializationFilter(final Node node, BeanDefinitionBuilder serializationConfigBuilder) {
BeanDefinitionBuilder filterConfigBuilder = createBeanBuilder(JavaSerializationFilterConfig.class);
for (Node child : childElements(node)) {
String name = cleanNodeName(child);
if ("blacklist".equals(name)) {
filterConfigBuilder.addPropertyValue("blacklist", createFilterListBean(child));
} else if ("whitelist".equals(name)) {
filterConfigBuilder.addPropertyValue("whitelist", createFilterListBean(child));
}
}
serializationConfigBuilder.addPropertyValue("javaSerializationFilterConfig",
filterConfigBuilder.getBeanDefinition());
}

private AbstractBeanDefinition createFilterListBean(Node node) {
BeanDefinitionBuilder filterListBuilder = createBeanBuilder(ClassFilter.class);
ManagedSet<String> classes = new ManagedSet<String>();
ManagedSet<String> packages = new ManagedSet<String>();
for (Node child : childElements(node)) {
String name = cleanNodeName(child);
if ("class".equals(name)) {
classes.add(getTextContent(child));
} else if ("package".equals(name)) {
packages.add(getTextContent(child));
}
}
filterListBuilder.addPropertyValue("classes", classes);
filterListBuilder.addPropertyValue("packages", packages);
return filterListBuilder.getBeanDefinition();
}
}
}
@@ -1753,6 +1753,46 @@
</xs:complexType>
</xs:element>

<xs:element name="java-serialization-filter">
<xs:complexType>
<xs:sequence>
<xs:element name="blacklist" type="filter-list" minOccurs="0" maxOccurs="1">
<xs:annotation>
<xs:documentation>
Blacklist used for deserialization class filtering.
</xs:documentation>
</xs:annotation>
</xs:element>
<xs:element name="whitelist" type="filter-list" minOccurs="0" maxOccurs="1">
<xs:annotation>
<xs:documentation>
Blacklist used for deserialization class filtering.
</xs:documentation>
</xs:annotation>
</xs:element>
</xs:sequence>
</xs:complexType>
</xs:element>

<xs:complexType name="filter-list">
<xs:choice minOccurs="0" maxOccurs="unbounded">
<xs:element name="class" type="xs:string" minOccurs="0" maxOccurs="unbounded">
<xs:annotation>
<xs:documentation>
Name of a class to be included in the list.
</xs:documentation>
</xs:annotation>
</xs:element>
<xs:element name="package" type="xs:string" minOccurs="0" maxOccurs="unbounded">
<xs:annotation>
<xs:documentation>
Name of a package to be included in the list.
</xs:documentation>
</xs:annotation>
</xs:element>
</xs:choice>
</xs:complexType>

<xs:complexType name="serialization-factory">
<xs:attributeGroup ref="class-or-bean-name"/>
<xs:attribute name="factory-id" type="xs:string" use="required"/>
@@ -2926,6 +2966,7 @@
<xs:element ref="data-serializable-factories" minOccurs="0" maxOccurs="1"/>
<xs:element ref="portable-factories" minOccurs="0" maxOccurs="1"/>
<xs:element ref="serializers" minOccurs="0" maxOccurs="1"/>
<xs:element ref="java-serialization-filter" minOccurs="0" maxOccurs="1"/>
</xs:sequence>
<xs:attribute name="use-native-byte-order" use="optional" type="xs:string" default="false"/>
<xs:attribute name="byte-order" use="optional" default="BIG_ENDIAN">
@@ -23,6 +23,7 @@
import com.hazelcast.config.CacheDeserializedValues;
import com.hazelcast.config.CacheSimpleConfig;
import com.hazelcast.config.CardinalityEstimatorConfig;
import com.hazelcast.config.ClassFilter;
import com.hazelcast.config.Config;
import com.hazelcast.config.CountDownLatchConfig;
import com.hazelcast.config.DiscoveryConfig;
@@ -40,6 +41,7 @@
import com.hazelcast.config.IcmpFailureDetectorConfig;
import com.hazelcast.config.InMemoryFormat;
import com.hazelcast.config.ItemListenerConfig;
import com.hazelcast.config.JavaSerializationFilterConfig;
import com.hazelcast.config.ListConfig;
import com.hazelcast.config.ListenerConfig;
import com.hazelcast.config.LockConfig;
@@ -1281,4 +1283,25 @@ public void testExplicitPortCountConfiguration() {

assertEquals(42, portCount);
}

@Test
public void testJavaSerializationFilterConfig() {
JavaSerializationFilterConfig filterConfig = config.getSerializationConfig().getJavaSerializationFilterConfig();
assertNotNull(filterConfig);

ClassFilter blacklist = filterConfig.getBlacklist();
assertNotNull(blacklist);
assertEquals(1, blacklist.getClasses().size());
assertTrue(blacklist.getClasses().contains("com.acme.app.BeanComparator"));
assertEquals(0, blacklist.getPackages().size());

ClassFilter whitelist = filterConfig.getWhitelist();
assertNotNull(whitelist);
assertEquals(2, whitelist.getClasses().size());
assertTrue(whitelist.getClasses().contains("java.lang.String"));
assertTrue(whitelist.getClasses().contains("example.Foo"));
assertEquals(2, whitelist.getPackages().size());
assertTrue(whitelist.getPackages().contains("com.acme.app"));
assertTrue(whitelist.getPackages().contains("com.acme.app.subpkg"));
}
}
@@ -551,6 +551,17 @@
<hz:serializer type-class="com.hazelcast.spring.serialization.DummySerializableObject2"
implementation="dummySerializer"/>
</hz:serializers>
<hz:java-serialization-filter>
<hz:blacklist>
<hz:class>com.acme.app.BeanComparator</hz:class>
</hz:blacklist>
<hz:whitelist>
<hz:class>java.lang.String</hz:class>
<hz:class>example.Foo</hz:class>
<hz:package>com.acme.app</hz:package>
<hz:package>com.acme.app.subpkg</hz:package>
</hz:whitelist>
</hz:java-serialization-filter>
</hz:serialization>

<hz:native-memory enabled="false" allocator-type="POOLED" metadata-space-percentage="10.2"
@@ -449,6 +449,8 @@ protected SerializationConfig parseSerialization(final Node node) {
fillPortableFactories(child, serializationConfig);
} else if ("serializers".equals(name)) {
fillSerializers(child, serializationConfig);
} else if ("java-serialization-filter".equals(name)) {
fillJavaSerializationFilter(child, serializationConfig);
}
}
return serializationConfig;
@@ -507,6 +509,34 @@ protected void fillSerializers(final Node node, SerializationConfig serializatio
}
}

protected void fillJavaSerializationFilter(final Node node, SerializationConfig serializationConfig) {
JavaSerializationFilterConfig filterConfig = new JavaSerializationFilterConfig();
serializationConfig.setJavaSerializationFilterConfig(filterConfig);
for (Node child : childElements(node)) {
final String name = cleanNodeName(child);
if ("blacklist".equals(name)) {
ClassFilter list = parseClassFilterList(child);
filterConfig.setBlacklist(list);
} else if ("whitelist".equals(name)) {
ClassFilter list = parseClassFilterList(child);
filterConfig.setWhitelist(list);
}
}
}

private ClassFilter parseClassFilterList(Node node) {
ClassFilter list = new ClassFilter();
for (Node child : childElements(node)) {
final String name = cleanNodeName(child);
if ("class".equals(name)) {
list.addClasses(getTextContent(child));
} else if ("package".equals(name)) {
list.addPackages(getTextContent(child));
}
}
return list;
}

protected void fillNativeMemoryConfig(Node node, NativeMemoryConfig nativeMemoryConfig) {
final NamedNodeMap atts = node.getAttributes();
final Node enabledNode = atts.getNamedItem("enabled");
ProTip! Use n and p to navigate between commits in a pull request.
You can’t perform that action at this time.