优秀的编程知识分享平台

网站首页 > 技术文章 正文

反序列化漏洞屡被黑客利用,危害巨大,代码怎样写才安全?

nanyue 2024-09-07 16:43:28 技术文章 9 ℃

反序列化漏洞出现很久了,一直到现在都很流行,以致OWASP组织将“不安全的反序列化”列为2017年10项最严重的Web 应用程序安全风险榜的第8位。

就在2017年12月22日和24日,国家信息安全漏洞共享平台(CNVD)连续发布了《于WebLogic Server WLS 组件存在远程命令执行漏洞的安全公告》第一版和第二版。漏洞编号为CNVD-2017-31499,对应CVE-2017-10271。

同时,在12月22日,各大安全网站都有报道称,黑客利用WebLogic 反序列化漏洞(CVE-2017-3248)和WebLogic WLS 组件漏洞(CVE-2017-10271)对企业服务器发起大范围远程攻击,有大量企业的服务器被攻陷。这两个漏洞都是反序列化漏洞,可见其危害之大。

因为网上已有大量的文章对这些漏洞进行了分析,并提供了修复方案,因此,我不再重复。

我这里关注的是,作为程序员,对反序列化代码要怎样写才安全?

序列化就是把对象转换成一种数据格式,如Json、XML等文本格式或二进制字节流格式,便于保存在内存、文件、数据库中或者在网络通信中进行传输。反序列化是序列化的逆过程,即由保存的文本格式或字节流格式还原成对象。

很多编程语言都提供了这一功能,但不幸的是,如果应用代码允许接受不可信的序列化数据,在进行反序列化操作时,可能会产生反序列化漏洞,黑客可以利用它进行拒绝服务攻击、访问控制攻击和远程命令执行攻击。

Java语言中实现安全地反序列化对象

Java中,ObjectOutputStream类的writeObject()方法实现序列化;ObjectInputStream类的readObject()方法用于反序列化。

(1)重载ObjectInputStream的resolveClass() 方法,只对允许的类进行反序列化操作。

代码示例如下:

public class LookAheadObjectInputStream extends ObjectInputStream {
public LookAheadObjectInputStream(InputStream inputStream) throws IOException {
super(inputStream);
}
/**
* 只反序列化Bicycle类
*/
@Override
protected Class<?> resolveClass(ObjectStreamClass desc) throws IOException,
ClassNotFoundException {
if (!desc.getName().equals(Bicycle.class.getName())) {
throw new InvalidClassException(
"Unauthorized deserialization attempt",
desc.getName());
}
return super.resolveClass(desc);
}
}

可以使用第三方提供的库,它们支持对className进行白名单、黑名单校验,如SerialKiller。

将你应用程序中类似下面的代码:

ObjectInputStream ois = new ObjectInputStream(is);
String msg = (String) ois.readObject();

替换成类似下面的代码,即用SerialKiller类替换ObjectInputStream类:

ObjectInputStream ois = new SerialKiller(is, "/etc/serialkiller.conf");
String msg = (String) ois.readObject();

serialkiller.conf文件中保存相关的配置信息,如黑名单、白名单等,示例如下:

<?xml version="1.0" encoding="UTF-8"?><!-- serialkiller.conf --><config>
<refresh>6000</refresh>
<mode> <!-- set to 'false' for blocking mode -->
<profiling>false</profiling>
</mode> <!-- if you're changing the logging settings, restart your app -->
<logging>
<enabled>true</enabled>
<logfile>/tmp/serialkiller.log</logfile>
</logging>
<blacklist> <!--Section for Regular Expressions-->
<regexps> <!-- ysoserial's BeanShell1 payload -->
<regexp>bsh\.XThislt;/regexp>
<regexp>bsh\.Interpreterlt;/regexp> <!-- ysoserial's C3P0 payload -->
<regexp>com\.mchange\.v2\.c3p0\.impl\.PoolBackedDataSourceBaselt;/regexp>	 <!-- ysoserial's MozillaRhino1 payload -->
<regexp>org\.mozilla\.javascript\..*lt;/regexp>
[...]
</regexps> <!--Section for full-package name-->
<list> <!-- ysoserial's CommonsCollections1,3,5,6 payload -->
<name>org.apache.commons.collections.functors.InstantiateTransformer</name>
<name>org.apache.commons.collections.functors.ConstantTransformer</name>
<name>org.apache.commons.collections.functors.ChainedTransformer</name>
<name>org.apache.commons.collections.functors.InvokerTransformer</name>
[...]
</list>
</blacklist>
<whitelist>
<regexps>
<regexp>.*</regexp>
</regexps>
</whitelist>
</config>

(2)防止反序列化域对象。

有些应用程序对象可能强制要求实现Serializable,为了保证这些应用程序对象不能被反序列化,可以将readObject()声明为final并且总是抛出异常。

private final void readObject(ObjectInputStream in) throws java.io.IOException {
throw new java.io.IOException("Cannot be deserialized");
}

(3)使用Agent代理加固所有java.io.ObjectInputStream的操作

前面提到的方法是通过子类化java.io.ObjectInputStream实现安全的反序列化操作,但是,如果你不拥有相关的代码或者等不及相关的补丁,那么这时使用一个Agent代理是最好的方式。但是,要从全局的角度对ObjectInputStream进行安全加固,只能使用黑名单校验,因为不太可能确定应用程序允许反序列化的所有类的清单来作为白名单,并且,这个黑名单要经常更新。

要启用Agent代理,添加以下JVM参数即可:

-javaagent:name-of-agent.jar

同样,也有一些第三方的库实现这个功能,如contrast-rO0等。

语言无关的安全反序列化方法

(1)使用纯数据格式

避免使用反序列化是减少风险的最好方式。使用纯数据格式如Json、XML,使数据对象和业务对象分离,这样减少了出现反序列化漏洞的机会。当然,在处理纯数据时,也有可能会出现漏洞。

(2)对反序列化数据进行签名

不要反序列化不可信的数据。在序列化时,对相关的数据进行签名,对没有通过验证的数据不进行反序列化。

最近发表
标签列表