必须在受控隔离类加载器中执行反序列化,禁用默认类加载器,配合RASP拦截与类型安全协议替代JDK原生序列化,以防止RCE和安全策略绕过。

在接收远程代理对象时做反序列化,必须防止恶意类通过默认类加载器污染应用上下文、触发 RCE 或绕过安全策略。核心不是“禁止反序列化”,而是让反序列化过程只在受控的类加载器中发生,且与主应用隔离。
明确反序列化入口点与类加载器归属
RPC 框架(如 Dubbo、gRPC-Java、RMI)在反序列化远程对象时,通常会使用当前线程上下文类加载器(Thread.currentThread().getContextClassLoader())或服务端默认类加载器。若未显式指定,就可能加载客户端传入的任意类——尤其是攻击者构造的 gadget 类。
- 检查框架反序列化入口:例如 Dubbo 的
JavaSerializationDeserializer、RMI 的UnicastRef.readExternal、或自定义的Serializer.deserialize(byte[], Class) - 确认该入口是否强制指定了安全类加载器(如白名单 ClassLoader),而非直接调用
ObjectInputStream.readObject()且依赖默认行为 - 避免使用
Thread.currentThread().setContextClassLoader()临时切换为不受信类加载器
构建专用隔离类加载器并限制类可见性
不能复用系统类加载器(AppClassLoader)或扩展类加载器(ExtClassLoader),而应创建一个无父委派、仅加载白名单类的定制加载器。
- 继承
ClassLoader,重写loadClass(String, boolean),禁用父委托(不调用super.loadClass) - 内部维护一个
Set<String> safeClasses,仅允许加载已知安全的类(如java.lang.String、java.util.ArrayList、业务 DTO 接口等) - 对非白名单类,直接抛出
ClassNotFoundException;对java.*、javax.*、sun.*等敏感包,一律拒绝加载 - 将该加载器作为
ObjectInputStream的子类构造参数传入,或通过反射设置其contextClassLoader字段
配合 RASP 或字节码钩子实现运行时拦截
仅靠类加载器隔离无法覆盖所有逃逸路径(如 readObject 中执行命令、static 块触发 URL 加载)。需在 JVM 运行时层加防护。
立即学习“Java免费学习笔记(深入)”;
- 使用 RASP 探针(如阿里云云安全中心、OpenRASP)Hook
ObjectInputStream.readObject、Class.forName、ClassLoader.loadClass等关键方法 - 在 Hook 回调中检查调用栈:若发现
readObject出现在 RPC 解码链路中,且目标类不在白名单内,立即中断并记录告警 - 对已知高危 gadget 类(如
org.apache.commons.collections.functors.InvokerTransformer)做硬编码拦截
禁用 JDK 原生反序列化,改用类型安全协议
最根本的防护是不让 ObjectInputStream 出现在生产链路中。
- RPC 层统一禁用
java.io.Serializable协议,强制使用 Protobuf、Avro 或 JSON Schema + Jackson(开启disable(DeserializationFeature.USE_LONG_FOR_INTS)等安全选项) - 若必须兼容旧协议,应在网关或 RPC 框架前置层做协议清洗:解析字节流头部,识别 JDK 序列化魔数
AC ED 00 05,直接拒绝或转交隔离沙箱处理 - 对远程代理接口(如 RMI 的
Remote子类),要求其实现类声明为final,且所有字段为基本类型或白名单包装类,杜绝动态代理/反序列化构造攻击面


















