当前位置: 首页 > news >正文

Java枚举类如何避免反射攻击和序列化攻击

前言:
我们知道实现单例模式的方式有三种,分别是:双重锁,静态内部类,枚举类
其中枚举类是最优雅,最安全的实现方式
相信绝大多数人最开始学的单例模式的实现就是双重锁的实现,这种方式虽然保证的线程安全和懒加载,但是仍然存在两个问题,会导致单例模式被破坏

  1. 通过反射可以获取到类的相关信息,访问到类的私有构造方法,再次创建一个实例对象,破坏单例模式
  2. 先序列化,再反序列化:每一次反序列化(字节流转换为Java对象)都会创建一个新的Java对象,也会破坏单例模式

枚举类可以很好的解决上述两种问题,原因如下:

  1. 枚举类无法通过反射获取私有方法,如果尝试获取就会抛出异常
  2. 枚举类实现了serilizable接口,反序列化创建的java对象始终唯一

以下是原理讲解


🧱 一、你需要知道的基本概念

🔑 1. 什么是反射攻击?

Java 的反射(Reflection)机制允许你在运行时访问类的私有构造方法,这就可能绕过单例模式的限制,创建多个实例。

👉 举例说明:

Constructor<Singleton> constructor = Singleton.class.getDeclaredConstructor();
constructor.setAccessible(true);
Singleton newInstance = constructor.newInstance();  // 又创建了一个新对象

🔑 2. 什么是序列化攻击?

当你对一个对象序列化后再反序列化,如果没有处理好 readResolve() 方法,可能会重新创建一个新的实例。

👉 举例说明:

ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("obj.ser"));
oos.writeObject(singleton);  // 序列化ObjectInputStream ois = new ObjectInputStream(new FileInputStream("obj.ser"));
Singleton newInstance = (Singleton) ois.readObject();  // 又是一个新对象

🚧 二、普通单例是如何被破坏的?

我们先看看一个普通单例是如何被攻击的:

🚫 普通单例类:

public class Singleton {private static final Singleton instance = new Singleton();private Singleton() {}public static Singleton getInstance() {return instance;}
}

🔨 利用反射:

Constructor<Singleton> constructor = Singleton.class.getDeclaredConstructor();
constructor.setAccessible(true);
Singleton another = constructor.newInstance();  // 💥 新的对象!

即使你用了 private 构造函数,反射还是可以绕过访问控制


✅ 三、枚举类如何防反射攻击?

🧠 枚举的底层原理(JVM限制)

枚举的构造方法会被编译器标记为 private,并且由 JVM 明确规定:

不能通过反射来创建枚举类的实例。

👉 举个例子:

public enum SingletonEnum {INSTANCE;
}

🔍 用反射尝试攻击:

Constructor<SingletonEnum> constructor = SingletonEnum.class.getDeclaredConstructor(String.class, int.class);
constructor.setAccessible(true);
SingletonEnum instance = constructor.newInstance("INSTANCE", 0);

🔥 运行时会抛出异常:

java.lang.IllegalArgumentException: Cannot reflectively create enum objects

✅ 原因:

JVM 源码中明确做了限制(在 Reflect.newInstance()ConstructorAccessorImpl.newInstance()):

if (clazz.isEnum()) {throw new IllegalArgumentException("Cannot reflectively create enum objects");
}

🚫 所以:枚举类实例无法通过反射创建,天然防御反射攻击。


✅ 四、枚举类如何防序列化攻击?

🔁 普通类的反序列化攻击原理

如果一个单例类实现了 Serializable,默认情况下每次反序列化都会创建新对象,除非你手动实现 readResolve() 方法。

🔍 漏洞示意:

public class Singleton implements Serializable {private static final Singleton instance = new Singleton();public static Singleton getInstance() { return instance; }
}

即使是单例,序列化再反序列化后:

Singleton s1 = Singleton.getInstance();
Singleton s2 = deserialize("singleton.obj");
System.out.println(s1 == s2);  // false ❌

🧠 而枚举类型在反序列化时自动保证单例!

public enum SingletonEnum {INSTANCE;
}

⚙ 枚举类自动实现了 Serializable,并且 JVM 源码中会调用:

return Enum.valueOf(enumType, name); // 返回同一个实例

即便你什么都不写,枚举的反序列化机制会自动返回枚举常量 INSTANCE始终是同一个对象


📘 来自《Effective Java》的权威背书

在《Effective Java》第三版第3条中,作者 Joshua Bloch 写道:

“枚举类型是实现单例的最佳方式。它简洁、自动支持序列化机制,并提供了对抗多重实例化攻击的绝对保障,即使面对复杂的反射和序列化攻击。”


✅ 总结:枚举单例的天然安全性

安全防护项普通单例需要额外处理枚举单例
线程安全❌(需加锁或静态内部类)✅ JVM 保证
反射攻击❌(可被破坏)✅ JVM 禁止
序列化攻击❌(需要重写 readResolve()✅ JVM 自动处理
写法简洁✅✅✅

🏁 如果你记住一句话:

枚举单例,是唯一能在 语言层面 自动防止反射 + 反序列化攻击的实现方式,也是 Java 实现单例模式的终极形态。


相关文章:

  • 零基础RT-thread第四节:电容按键
  • 自动化性能回退机制——蓝绿部署与灰度发布
  • Electron (02)集成 SpringBoot:服务与桌面程序协同启动方案
  • 回文链表C++
  • 设计模式精讲 Day 8:组合模式(Composite Pattern)
  • Transformer实战——Hugging Face环境配置与应用详解
  • 什么是seata
  • node.js在vscode的配置
  • 多线程八股
  • 【小程序】如何生成特定页面的小程序码
  • 代码审计-fastjson反序列化漏洞
  • .NET基于类名约定的自动依赖注入完整指南
  • python+uniapp基于微信小程序的高校二手商品交易系统
  • NVR的方法多种取决于应用场景
  • PVE使用ubuntu-cloud-24.img创建虚拟机并制作模板
  • 20250620在荣品的PRO-RK3566开发板的Android13系统的uboot阶段就拉高GPIO2C6【driver模式】
  • 第3讲、LangChain性能优化:上下文缓存与流式响应实战指南
  • java面试题03静态修饰类,属性,方法有什么特点?
  • Maven并行构建
  • Anaconda安装env,yml一直卡在Solving environment:不动
  • 网站建设销售员工作内容/有哪些可以免费推广的平台
  • 焦作建设厅网站/西安关键词排名提升
  • 网站制作价格表模板/外链大全
  • 前端开发工程师需要考什么证/宁波seo网站推广
  • 南阳网站建设/网站的友情链接是什么意思
  • 网站搬家/长尾关键词举例