本文目录导读:

反序列化漏洞的防御是一个系统性工程,核心原则是:永远不要反序列化不可信的数据,但由于业务需求,很多场景必须处理外部数据,防御策略需要从代码编写、架构设计到运行时监控进行多层防护。
以下是针对反序列化漏洞的详细防御措施,分为通用策略、语言特定措施、架构级防护和监控与修复四个层面。
核心原则:拒绝不可信数据
这是最根本、最有效的防御。
- 危险场景:HTTP请求中的Cookie、Session、POST Body;消息队列中的对象;文件上传的序列化数据。
- 防御动作:如果业务无需处理外部传入的序列化数据,直接拒绝并抛出异常。
代码与语言层面防御(具体实现)
使用安全的序列化格式(强烈推荐)
罪魁祸首:Java原生序列化(ObjectInputStream)、PHP的unserialize()、Python的pickle(),这些格式不仅携带数据,还携带执行逻辑(Gadget Chains)。
替代方案:使用只处理纯数据的格式,无法触发任何代码执行。
| 不安全格式 | 安全替代方案 |
|---|---|
| Java ObjectInputStream | JSON (Jackson, Gson)、Protocol Buffers (Protobuf)、Avro |
PHP unserialize() |
JSON (json_decode) 或结构化的序列化方案 |
Python pickle |
JSON、YAML (安全加载)、MessagePack |
.NET BinaryFormatter |
JSON、XML、Protobuf |
- 注意:在使用JSON等库时,也要注意配置,避免因多态类型(Polymorphic Type Handling)导致的攻击,Jackson的
enableDefaultTyping()功能就是CVE的常见来源。请务必禁用自动类型绑定或开启白名单校验。
如果必须使用原生序列化(Java/JVM 重点)
如果无法更换格式,必须采取以下硬性措施:
-
严格白名单校验:
- 不要试图黑名单(如
Runtime、ProcessBuilder),因为Gadget Chian太多。 - 只允许已知安全的类,在
ObjectInputStream中重写resolveClass()方法,检查反序列化的类是否在白名单内。
// Java示例:白名单校验 public class SafeObjectInputStream extends ObjectInputStream { private static final Set<String> WHITELIST = Set.of( "com.yourcompany.model.User", "java.util.ArrayList", "com.yourcompany.dto.Order" // 只允许你业务需要的具体类 ); protected Class<?> resolveClass(ObjectStreamClass desc) throws IOException, ClassNotFoundException { if (!WHITELIST.contains(desc.getName())) { throw new InvalidClassException("Unauthorized deserialization attempt", desc.getName()); } return super.resolveClass(desc); } } - 不要试图黑名单(如
-
使用 Look-ahead ObjectInputStream (LAOIS) 或类似技术:
- 社区提供的增强版
ObjectInputStream,在读取对象前就进行了类校验和容量限制。
- 社区提供的增强版
-
Java 9+ 的模块化系统:
限制哪些模块可以导出/开放序列化操作。
使用安全的Hibernate/ORM配置
- 如果使用Hibernate或MyBatis,确保反序列化由框架内部自动处理,且不会将用户输入直接作为反序列化对象。
限制反序列化资源消耗
- 设置大小限制:拒绝超大字节流(例如超过10MB的序列化数据直接拒绝)。
- 设置超时:反序列化操作通常很快,如果出现长时间CPU占用,大概率是工具类在构造复杂对象,应该强行中断。
- 限制递归深度:防止深度嵌套对象导致的栈溢出(StackOverflow)或对象构造死循环。
架构与环境级防御
最小权限原则
- 不要以 root/管理员权限运行应用,即使攻击者成功反序列化执行了代码(如
Runtime.exec("rm -rf /")),低权限用户也无法造成毁灭性打击。 - 使用沙箱/容器:Docker容器或Java SecurityManager(虽然Java17后弃用,但可以考虑替代方案)。
网络隔离
- 禁止直接暴露原生序列化接口,如果你的应用使用了RMI、JMX、EJB等协议,确保它们只在内部网络访问,绝不能暴露在公网。
- 使用Web应用防火墙(WAF):
- 配置规则检测常见的反序列化利用特征(如Java序列化的魔术字节
ACED0005或rO0AB),进行阻断。 - 注意:攻击者可以通过Base64编码、压缩等方式绕过,WAF只是辅助。
- 配置规则检测常见的反序列化利用特征(如Java序列化的魔术字节
组件升级(修复已知漏洞)
这是最基础也是最重要的一步:
- 关注依赖库(Java重点):
- Apache Commons Collections(所有版本都有问题,但3.2.2和4.1修复了最危险的链)。
- Jackson(及时升级到最新版本,禁用
defaultTyping)。 - Fastjson(历史上有多起严重的反序列化漏洞,请使用白名单模式而非AutoType)。
- Spring Framework(尤其是在解析
@RequestBody或数据绑定时的漏洞)。
- 使用OWASP Dependency Check或Snyk:自动扫描依赖库,发现已知的CVE并提示升级。
运行时监控与检测
- 审计日志:
- 记录所有反序列化操作来源(IP、User-Agent、Session)。
- 设置告警:当反序列化动作出现异常频率时触发告警。
- 端点检测与响应(EDR):
- 监控JVM或进程中的异常行为,如:突然执行命令(
exec)、加载JNDI资源(JNDI注入是反序列化的常见落地手法)、写入文件等。
- 监控JVM或进程中的异常行为,如:突然执行命令(
- Cloudflare/CloudFront等边缘计算:部分服务原生支持检测序列化攻击载荷。
针对特定语言的额外建议
Java
- 禁用RMI over HTTP。
- 慎用JNDI:如果必须使用,升级JDK(8u191+ 有默认的
com.sun.jndi.ldap.object.trustURLCodebase=false)。 - Filter模式:使用Java
ObjectInputFilter(Java 9+),在全局或流级别设置黑名单/白名单。
PHP
- 使用
hash_hmac包装Session数据。 - 不要使用
$_COOKIE['data']直接unserialize()。
Python
- 不要使用
pickle.loads()处理外部数据,即使是内部调用,也尽量用JSON。 - 如果必须用
pickle,确保数据来源经过强身份验证和加密。
.NET
- 移除
BinaryFormatter:微软已官方标记其不安全,建议完全禁止。 - 使用
NetDataContractSerializer:但需要配合白名单。
防御优先级(从上到下)
- 不用原生序列化:换成JSON/Protobuf。(最佳方案,能解决90%的问题)
- 必须用时用白名单:严格限制可反序列化的类。
- 资源限制:控制大小、超时、深度。
- 环境隔离:最小权限、沙箱、网络隔离。
- 升级依赖:修复已知的第三方库漏洞。
- 监控告警:及时发现并阻断攻击。
核心思考:反序列化攻击的核心是利用了“数据类型是动态且可执行的”这一特性,如果你只能传递纯文本(JSON),攻击者就无法注入恶意对象。数据与代码的分离是防御的根本。