Java反序列化中基本类型默认值滥用漏洞不依赖gadget chain,防御需改用包装类型+null校验、重写readObject强制完整性验证,并严格配置ObjectInputFilter。

Java反序列化漏洞中,利用基本类型默认值(如 int 默认为 0、boolean 默认为 false)进行参数注入,本质是攻击者构造恶意序列化流,绕过业务层校验逻辑,使未显式赋值的字段被 JVM 自动初始化为默认值,从而触发非预期行为(例如跳过权限检查、绕过空值判断、触发默认分支逻辑)。这类问题**不依赖 gadget chain**,因此传统黑名单/白名单类反序列化过滤器(如 `ObjectInputFilter`)无法直接拦截——因为基本类型本身合法,其默认值也合法。
理解漏洞根源:默认值不是“注入”,而是“未赋值”被误用
关键点在于:JVM 反序列化时对基本类型字段不做校验,直接使用默认值;而业务代码若写成类似以下逻辑,就构成风险:
if (user.isAdmin == false) { // 若未反序列化赋值,此处为 false,误判为普通用户
denyAccess();
}
这不是反序列化“注入”了 false,而是字段根本没被设值,JVM 填了默认值。因此防御不能只靠“过滤输入”,更要从设计和校验层面堵住逻辑缺口。
启用并严格配置 ObjectInputFilter(JDK 9+)
虽然它不拦基本类型默认值,但可防止恶意类加载和 gadget 利用,是基础防线。必须显式设置,不可依赖默认策略(默认允许一切):
立即学习“Java免费学习笔记(深入)”;
- 全局设置(推荐在应用启动时一次配置):
System.setProperty("jdk.serialFilter", "maxdepth=5;maxarray=100000;package.whitelist.*;!*"); - 对每个
ObjectInputStream实例单独设置更安全:ois.setObjectInputFilter(ObjectInputFilter.Config.createFilter("my.app.model.**;!*")); - 务必禁用通配符泛滥(如
java.**)、限制深度与数组大小,避免 DOS 或 gadget 加载。
用包装类型替代基本类型,并做显式 null 检查
这是针对默认值滥用最有效的编码改进:
- 将
boolean isAdmin改为Boolean isAdmin,int age改为Integer age - 反序列化后,立即检查关键字段是否为
null:if (user.isAdmin == null) throw new InvalidObjectException("isAdmin must be explicitly set"); - 配合 Jackson/Gson 等 JSON 库时,也可开启
DeserializationFeature.FAIL_ON_NULL_FOR_PRIMITIVES,但注意这仅适用于 JSON,不适用于原生 Java 序列化。
重写 readObject() 并强制校验字段完整性
在可序列化类中定义私有 readObject 方法,实现细粒度控制:
private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
in.defaultReadObject(); // 先完成默认反序列化
if (this.isAdmin == null) {
throw new InvalidObjectException("Field 'isAdmin' is missing or null");
}
if (this.age == null || this.age <= 0 || this.age > 150) {
throw new InvalidObjectException("Invalid 'age' value");
}
}
该方法会在每次反序列化时执行,确保所有关键字段被显式设置且符合业务约束,从根本上规避默认值误用。


















