我正在阅读有效的 Java 作者:Joshua Bloch https://www.amazon.com.au/Effective-Java-Joshua-Bloch/dp/0134685997/. In 第 8 项:避免定型剂和清洁剂 of 第2章他说:
终结器有一个严重的安全问题:它们将你的类开放给
终结器攻击。终结器攻击背后的想法很简单:如果
构造函数或其序列化抛出异常
等价物——readObject
and readResolve
方法(第 12 章)——
恶意子类的终结器可以在部分构造的上运行
应该“死在藤蔓上”的对象。这个终结器可以记录
对静态字段中对象的引用,防止它被
垃圾收集。一旦畸形对象被记录下来,它就会被记录下来。
调用该对象上的任意方法是一件简单的事情,应该
从一开始就没有被允许存在。扔一个
构造函数的异常应该足以阻止对象
从出现开始;在存在终结器的情况下,则不是。
此类攻击可能会造成可怕的后果。最终课程不受
终结器攻击,因为没有人可以编写终结器的恶意子类
最后一堂课。
首先,我知道终结器已经已弃用 https://stackoverflow.com/a/56454348/6367109从 Java 18 开始。不过,我认为理解这个决定背后的原因很重要。我对上面摘录的理解如下:
- 终结器是不确定的。
- 恶意子类可以在部分构造的损坏的超类对象上运行其终结器方法。
- 将损坏对象的引用移至静态字段不会让 JVM 进行垃圾收集。
- 攻击者可以使用这个本应“死在藤蔓上”的对象并为所欲为。因此,存在安全缺陷。
其次,我希望我对这个问题的概念理解是正确的。然而,Bloch 尚未在具体的代码示例中演示这个问题。也许是因为他不想让我们搞乱最终确定机制Object
.
您能用代码向我演示一下吗?
例如,如果我有一个超类:
/** Superclass */
public class DemoSecurityProblem {
}
然后通过继承或组合来子类:
public class MaliciousSubClass extends DemoSecurityProblem {
DemoSecurityProblem demoSecurityProblem = new DemoSecurityProblem();
}
攻击者如何通过终结机制利用这一点?
多谢!
你的描述基本上是正确的,但事情过于复杂。不需要在 a 中存储任何东西static
多变的;一旦finalize()
方法被调用时,该对象已经复活,因为调用对象上的方法意味着调用可以访问该对象的代码。
将对象引用存储在变量中是一种将生命周期扩展到超出执行范围的方法finalize()
方法但这不是攻击所必需的。另外,不要使用static
变量,攻击者还可以使子类成为内部类,并将引用存储在仍然可达的外部对象中。
所以下面的程序已经足以说明问题了
public class FinalizerAttackExample {
public static void main(String[] args) throws InterruptedException {
try {
new MaliciousSubclass();
} catch(SecurityException ex) {
System.out.println("wouldn't get hands on a ResourceClass instance");
}
System.gc();
Thread.sleep(2000);
}
static class ResourceClass {
ResourceClass() {
if(!checkCaller()) throw new SecurityException();
}
public void criticalAction() {
System.out.println("ResourceClass.criticalAction()");
}
}
/** For our demonstration, all callers are invalid */
static boolean checkCaller() {
return false;
}
static class MaliciousSubclass extends ResourceClass {
@Override
protected void finalize() {
System.out.println("see, I got hands on " + this);
criticalAction();
}
}
}
虽然垃圾收集是不确定的,并且通常不能保证终结器的执行,但此示例将打印
wouldn't get hands on a ResourceClass instance
see, I got hands on FinalizerAttackExample$MaliciousSubclass@7ad74083
ResourceClass.criticalAction()
在很多实现上,证明了criticalAction()
可以在一个不应该存在的对象上调用,因为构造函数抛出了异常。
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)