Java终结器安全问题解析及攻击代码演示请求
关于Finalizer攻击的理解确认与代码演示
你的理解完全正确
你总结的4点核心问题,正是终结器被废弃以及finalizer攻击的关键原因:
- 终结器具有不确定性:其执行时间由GC决定,无法预测,可能导致资源泄露或逻辑异常
- 恶意子类可在部分构造的损坏父类对象上运行其终结器方法:父类构造失败时,子类的终结器仍可能被触发
- 将损坏对象的引用存入静态字段会阻止JVM垃圾回收:静态字段属于类级别,不会被GC回收,从而保留了本应销毁的对象
- 攻击者可利用本应销毁的对象实施操作,引发安全漏洞:未完全初始化的对象可能执行敏感逻辑,破坏系统安全性
Finalizer攻击代码演示
基于你提供的类结构,我们改造后实现完整的攻击场景:
存在安全风险的父类
/** 存在安全风险的父类 */ public class DemoSecurityProblem { public DemoSecurityProblem() { // 模拟构造过程中抛出异常,导致对象未完全初始化 throw new RuntimeException("构造失败,对象未完全初始化"); } // 假设的敏感业务方法,仅允许完全初始化的对象调用 public void doSensitiveOperation() { System.out.println("执行敏感操作:" + this); } }
恶意子类
public class MaliciousSubClass extends DemoSecurityProblem { // 静态字段保存泄漏的未初始化对象,阻止GC回收 private static DemoSecurityProblem leakedInstance; public MaliciousSubClass() { // 父类构造抛出异常,子类构造流程中断,但对象已部分创建 } @Override protected void finalize() throws Throwable { // 将当前未完全初始化的对象存入静态字段 leakedInstance = this; System.out.println("终结器被调用,已泄漏未初始化对象"); super.finalize(); } // 攻击者利用泄漏对象的入口方法 public static void exploit() { if (leakedInstance != null) { System.out.println("利用泄漏的未初始化对象:"); leakedInstance.doSensitiveOperation(); } } }
攻击测试类
public class FinalizerAttackDemo { public static void main(String[] args) throws InterruptedException { try { // 尝试创建子类对象,触发父类构造异常 new MaliciousSubClass(); } catch (RuntimeException e) { System.out.println("捕获构造异常:" + e.getMessage()); } // 触发GC,促使终结器执行 System.gc(); // 等待终结器线程完成(实际中终结器执行时间不确定,此处用sleep模拟) Thread.sleep(1000); // 执行攻击逻辑 MaliciousSubClass.exploit(); } }
代码执行流程说明
- 当尝试创建
MaliciousSubClass实例时,父类构造抛出异常,对象未完全初始化,按正常逻辑应被GC回收。 - GC触发后,JVM发现该对象未被任何可达引用指向,准备回收时调用子类重写的
finalize方法,将对象存入静态字段leakedInstance,使其成为可达状态,阻止GC回收。 - 攻击者通过
exploit方法获取该未初始化对象,调用其敏感方法,实现攻击。
防御方式验证
如果将父类声明为final:
public final class DemoSecurityProblem { // 原有代码不变 }
此时MaliciousSubClass无法继承该类,也就无法重写finalize方法,从根源上避免了这类攻击,这也印证了《Effective Java》中提到的final类免疫finalizer攻击的结论。
内容的提问来源于stack exchange,提问作者Rahul
相关产品推荐
相关产品推荐

