Java RMI安全核心是严格控制反序列化入口、限制可加载类、避免信任不可控输入;需用serialFilter白名单、禁用高危包、改用JSON/HTTP或gRPC替代原生序列化,并规范POJO实现与参数类型。

Java 中实现安全的远程对象调用序列化,核心不是“完全禁用序列化”,而是**在必须使用 RMI 的前提下,严格控制反序列化入口、限制可加载类、避免信任不可控输入**。RMI 本身依赖 Java 原生序列化传输对象,而原生序列化机制缺乏内置校验,一旦服务端反序列化恶意构造的字节流(如含 Commons Collections 等 gadget 链),极易触发远程代码执行(RCE)。
明确 RMI 的序列化风险点
RMI 的通信流程中,以下环节涉及反序列化且存在攻击面:
- 客户端 lookup() 获取远程 stub 时,注册中心(Registry)返回的 stub 对象需反序列化 —— 若 Registry 被劫持或配置不当,可能返回恶意 stub;
- 客户端调用远程方法时,参数对象被序列化发往服务端;
- 服务端接收参数后必须反序列化 —— 这是最关键、最危险的反序列化点;
- 服务端返回结果给客户端,客户端也要反序列化返回值 —— 若服务端被攻陷,可返回恶意 payload。
服务端加固:限制反序列化范围
Java 9+ 提供了 serialFilter 机制,是目前最直接有效的防护手段。它允许你白名单式声明哪些类允许被反序列化:
- 启动服务端 JVM 时添加参数:-Dsun.rmi.transport.tcp.handshakeTimeout=5000 -Dsun.rmi.transport.tcp.responseTimeout=15000(增强超时防御);
- 更关键的是设置过滤器:-Djdk.serialFilter="maxdepth=20;maxrefs=20;maxbytes=1000000;whitelist:java.time.*,cn.mycompany.dto.**,java.lang.String,java.lang.Integer";
- 务必排除
java.util.*、org.apache.commons.collections.*、groovy.lang.*等高危包; - 若使用 Java 8,可通过
ObjectInputStream.resolveClass()重写逻辑,在自定义的RMIServerSocketFactory中拦截非法类名。
替代方案:绕过原生序列化
真正提升安全性,应从架构层面减少对 Java 原生序列化的依赖:
立即学习“Java免费学习笔记(深入)”;
- 用 JSON + HTTP 替代 RMI:将远程接口封装为 RESTful API(如 Spring Boot Web),用 Jackson/Fastjson 序列化,天然规避
ObjectInputStream攻击面; - 采用 gRPC:基于 Protocol Buffers,强类型、二进制高效,无 Java 反序列化风险;
- 若仍需 RMI 协议语义,可用 Apache Avro 或 FlatBuffers 自定义序列化层,配合 RMI Stub/Stubless 模式,把反序列化逻辑收归可控的解析器;
- 禁止 RMI Registry 暴露在公网:仅限内网通信,并通过防火墙限制 1099 端口访问源 IP。
开发与部署规范
技术手段之外,流程管控同样关键:
- 所有参与 RMI 通信的 POJO 类必须显式实现
Serializable,并添加private static final long serialVersionUID = 1L;,防止因类结构变更导致意外反序列化失败或行为异常; - 远程接口方法参数和返回值尽量使用 JDK 基础类型(String、Long、List<String>)、不可变对象或自定义 DTO,避免传入
File、Thread、Runtime等敏感类; - 服务端不要直接反序列化客户端传来的任意
Object参数 —— 应强制约定具体类型,用ObjectInputStream.readObject()后立即做 instanceof 校验; - 定期更新 JDK,及时修复已知反序列化漏洞(如 JDK 17+ 对 filter 机制持续增强)。


















