反序列化漏洞如何防御

wen 网络安全 29

本文目录导读:

反序列化漏洞如何防御

  1. 核心原则:拒绝不可信数据
  2. 代码与语言层面防御(具体实现)
  3. 架构与环境级防御
  4. 运行时监控与检测
  5. 针对特定语言的额外建议
  6. 总结:防御优先级(从上到下)

反序列化漏洞的防御是一个系统性工程,核心原则是:永远不要反序列化不可信的数据,但由于业务需求,很多场景必须处理外部数据,防御策略需要从代码编写、架构设计到运行时监控进行多层防护。

以下是针对反序列化漏洞的详细防御措施,分为通用策略语言特定措施架构级防护监控与修复四个层面。


核心原则:拒绝不可信数据

这是最根本、最有效的防御。

  • 危险场景: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 JSONYAML (安全加载)、MessagePack
.NET BinaryFormatter JSONXMLProtobuf
  • 注意:在使用JSON等库时,也要注意配置,避免因多态类型(Polymorphic Type Handling)导致的攻击,Jackson的enableDefaultTyping()功能就是CVE的常见来源。请务必禁用自动类型绑定或开启白名单校验。

如果必须使用原生序列化(Java/JVM 重点)

如果无法更换格式,必须采取以下硬性措施:

  • 严格白名单校验

    • 不要试图黑名单(如RuntimeProcessBuilder),因为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序列化的魔术字节 ACED0005rO0AB),进行阻断。
    • 注意:攻击者可以通过Base64编码、压缩等方式绕过,WAF只是辅助。

组件升级(修复已知漏洞)

这是最基础也是最重要的一步:

  • 关注依赖库(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注入是反序列化的常见落地手法)、写入文件等。
  • 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:但需要配合白名单。

防御优先级(从上到下)

  1. 不用原生序列化:换成JSON/Protobuf。(最佳方案,能解决90%的问题)
  2. 必须用时用白名单:严格限制可反序列化的类。
  3. 资源限制:控制大小、超时、深度。
  4. 环境隔离:最小权限、沙箱、网络隔离。
  5. 升级依赖:修复已知的第三方库漏洞。
  6. 监控告警:及时发现并阻断攻击。

核心思考:反序列化攻击的核心是利用了“数据类型是动态且可执行的”这一特性,如果你只能传递纯文本(JSON),攻击者就无法注入恶意对象。数据与代码的分离是防御的根本。

抱歉,评论功能暂时关闭!