序列化漏洞如何检测修复

wen 开源项目 20

本文目录导读:

序列化漏洞如何检测修复

  1. 如何检测序列化漏洞
  2. 如何修复序列化漏洞
  3. 检测与修复优先级

序列化漏洞(通常指反序列化漏洞)是Java、PHP、.NET等语言中非常危险的高危漏洞,攻击者可以通过构造恶意序列化数据,在服务端反序列化时触发任意代码执行、命令执行或拒绝服务。

以下从检测修复两个维度,结合主流语言(Java、PHP、Python、.NET)给出具体方案。


如何检测序列化漏洞

代码审计(静态检测)

Java 重点搜索:

  • ObjectInputStream.readObject()
  • ObjectInputStream.readUnshared()
  • readObject() 方法自定义实现的类
  • 使用了 RMIJMXJMSHessianKryoXStreamFastjsonJackson 等框架的默认反序列化

PHP 重点搜索:

  • unserialize() 函数
  • __wakeup()__destruct()__toString() 魔术方法
  • 使用了 phpggc 等工具可以生成 gadget chain 的库

Python 重点搜索:

  • pickle.load() / pickle.loads()
  • 使用了 PyYAMLruamel.yaml 且未限制 Loader 类型
  • jsonpickle 等库

C# / .NET 重点搜索:

  • BinaryFormatter.Deserialize()
  • SoapFormatter.Deserialize()
  • NetDataContractSerializer
  • JavaScriptSerializer.Deserialize()(旧版)
  • TypeNameHandling 设置为 AutoAllNewtonsoft.Json

动态测试(黑盒/灰盒)

网络流量抓包:

  • 使用 Burp Suite、Wireshark 抓取 HTTP 请求体、Cookie、隐藏字段
  • 查找以下特征:
    • 二进制序列化开头:aced0005(Java 序列化魔数)
    • PHP 序列化字符串:a:1:{s:4:...O:8:
    • Base64 编码的序列化内容(解码后检查魔数)
    • 自定义编码后的序列化数据

工具辅助检测:

  • Java: ysoserial 生成 payload,逐个尝试不同的 gadget chain
  • PHP: phpggc 生成 payload
  • Python: pickle 恶意构造
  • 通用: 使用 SerializationDumper 解码二进制序列化,判断是否可被利用

安全扫描器:

  • Burp Suite 的 Java Deserialization Scanner 插件(自动检测 JDK 版本、gadget chain)
  • Nessus / OpenVAS 中相关的反序列化插件

日志监测

  • 检查日志中是否有大量 ClassNotFoundExceptionIOException 异常
  • 检查是否有 readObject 调用导致的异常堆栈,特别是涉及 RuntimeProcessBuilderFileInputStream 的调用

如何修复序列化漏洞

最彻底:避免使用不安全的反序列化

  • 不使用 ObjectInputStreamunserialize()pickle.loads() 直接处理不可信数据
  • 改用安全的格式替代:
    • JSON(使用 JacksonGson,关闭 enableDefaultTyping
    • Protocol Buffers(gRPC)
    • MessagePack(但需注意实现是否安全)
    • YAML(需使用 SafeLoader,如 yaml.safe_load()

输入校验与验证

  • 白名单验证: 反序列化前校验数据类型、类名是否在允许列表中

    • Java 示例:
      ObjectInputStream ois = new ObjectInputStream(input) {
          @Override
          protected Class<?> resolveClass(ObjectStreamClass desc) throws IOException, ClassNotFoundException {
              // 只允许反序列化指定的安全类
              if (!desc.getName().equals("com.example.SafeUser")) {
                  throw new InvalidClassException("Unauthorized deserialization", desc.getName());
              }
              return super.resolveClass(desc);
          }
      };
  • 签名校验(HMAC): 对序列化数据进行完整性校验,防止被篡改

    // 写入时
    byte[] data = serialize(user);
    byte[] hmac = computeHmac(data, secretKey);
    send(data + hmac);
    // 读取时
    byte[] receivedData = ...;
    byte[] receivedHmac = ...;
    if (!verifyHmac(receivedData, receivedHmac, secretKey)) {
        throw new SecurityException("数据被篡改");
    }
    Object obj = deserialize(receivedData);

使用安全的反序列化工具或过滤机制

Java:

  • 使用 SerialKiller:开源的 Java 反序列化防火墙,支持黑/白名单
  • 使用 ValidatingObjectInputStream(Apache Commons IO)
  • 使用 HessianKryo(需关闭默认的 auto-type 功能)
  • 使用 FastjsonSafeMode(1.2.68+)
    ParserConfig.getGlobalInstance().setSafeMode(true);

PHP:

  • 使用 hash_equals 对序列化字符串做 HMAC 校验
  • 尽量使用 json_decode + json_encode 替代
  • 升级 PHP 版本,某些旧版本自动反序列化问题更严重

Python:

  • 永远不要对不可信数据使用 pickle
  • 使用 yaml.safe_load() 代替 yaml.load()
  • 使用 json + 自定义 object_hook 控制反序列化行为

限制反序列化来源

  • 不要将反序列化接口暴露在公网,仅限内网或经过认证的客户端使用
  • 添加 IP 白名单、Token 校验、CSRF 防护
  • 对反序列化的数据大小做上限限制(防止内存耗尽攻击)
    // 限制数据大小
    byte[] data = readFromSocket();
    if (data.length > 1024 * 1024) { // 1MB
        throw new SecurityException("数据过大");
    }

升级依赖库版本

很多反序列化漏洞源于第三方库的 gadget chain:

已知漏洞版本(示例) 安全版本(示例)
Commons Collections ≤ 3.2.1 2.2+
Commons BeanUtils ≤ 1.9.3 9.4+
Fastjson ≤ 1.2.24 2.83+
Jackson 默认开启enableDefaultTyping 关闭或升级
Spring Framework ≤ 4.2.4 3+

使用 OWASP Dependency CheckSnyk 自动检测依赖中的已知漏洞。

使用 RASP(运行时应用自我保护)工具

  • OpenRASP(百度开源):在运行时拦截 readObjectRuntime.exec 等敏感调用
  • Contrast SecurityAqua Security:商业级 RASP 产品

针对特定语言的最佳实践

Java 示例(综合方案):

// 1. 使用安全的ObjectInputStream
public class SafeObjectInputStream extends ObjectInputStream {
    private static final Set<String> WHITELIST = Set.of(
        "java.lang.String",
        "java.util.ArrayList",
        "com.example.SafeUser"
    );
    public SafeObjectInputStream(InputStream in) throws IOException {
        super(in);
    }
    @Override
    protected Class<?> resolveClass(ObjectStreamClass desc) throws IOException, ClassNotFoundException {
        if (!WHITELIST.contains(desc.getName())) {
            throw new InvalidClassException("Blocked class: " + desc.getName());
        }
        return super.resolveClass(desc);
    }
}
// 2. HMAC 校验
public static byte[] serializeWithHmac(Serializable obj, byte[] key) throws Exception {
    ByteArrayOutputStream bos = new ByteArrayOutputStream();
    ObjectOutputStream oos = new ObjectOutputStream(bos);
    oos.writeObject(obj);
    oos.close();
    byte[] data = bos.toByteArray();
    Mac mac = Mac.getInstance("HmacSHA256");
    mac.init(new SecretKeySpec(key, "HmacSHA256"));
    byte[] hmac = mac.doFinal(data);
    ByteArrayOutputStream finalBos = new ByteArrayOutputStream();
    finalBos.write(data);
    finalBos.write(hmac);
    return finalBos.toByteArray();
}

检测与修复优先级

优先级 动作 说明
最高 替换为安全格式(JSON/Protobuf) 一劳永逸,彻底根除反序列化风险
升级依赖库版本 防止已知的gadget chain利用
添加HMAC签名校验 防止数据篡改,降低利用复杂度
实现白名单类验证 适用于必须使用序列化场景
使用RASP工具运行时检测 适合历史遗留系统无法快速改造
仅限制来源IP/认证 不能单独作为防护,需与其他配合

最终的铁律:永远不要对不可信数据直接进行反序列化。

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