为什么 Java 终结器存在安全问题?

2024-04-06

我正在阅读有效的 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 开始。不过,我认为理解这个决定背后的原因很重要。我对上面摘录的理解如下:

  1. 终结器是不确定的。
  2. 恶意子类可以在部分构造的损坏的超类对象上运行其终结器方法。
  3. 将损坏对象的引用移至静态字段不会让 JVM 进行垃圾收集。
  4. 攻击者可以使用这个本应“死在藤蔓上”的对象并为所欲为。因此,存在安全缺陷。

其次,我希望我对这个问题的概念理解是正确的。然而,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(使用前将#替换为@)

为什么 Java 终结器存在安全问题? 的相关文章

  • 通过SOCKS代理连接Kafka

    我有一个在 AWS 上运行的 Kafka 集群 我想用标准连接到集群卡夫卡控制台消费者从我的应用程序服务器 应用程序服务器可以通过 SOCKS 代理访问互联网 无需身份验证 如何告诉 Kafka 客户端通过代理进行连接 我尝试了很多事情 包
  • 使用 GWT 读取非常大的本地 XML 文件

    我正在使用 GWT 构建我的第一个 Java 应用程序 它必须从一个非常大的 XML 文件中读取数据 当我尝试发送对文件中信息的请求时遇到问题 并且我不太确定它是否与文件的大小或我的语义有关 在我的程序中 我有以下内容 static fin
  • “java.net.MalformedURLException:未找到协议”读取到 html 文件

    我收到一个错误 java net MalformedURLException Protocol not found 我想读取网络上的 HTML 文件 mainfest uses permission android name android
  • 打印星号的 ASCII 菱形

    我的程序打印出这样的钻石 但只有当参数或菱形的每一面为4 例如如果我输入6 底部三角形的间距是错误的 我一直在试图找出答案 当参数改变时 底部的三角形不会改变 只有顶部的三角形会改变 它只适用于输入4 public static void
  • 不同类型的数组

    是否可以有一个包含两种不同类型数据的数组 我想要一个包含双精度型和字符串的数组 我尝试过 ArrayList
  • 什么时候通过引用传递不是一个好主意?

    这是一个我从未真正理解的内存分配问题 void unleashMonkeyFish MonkeyFish monkey fish new MonkeyFish std string localname Wanda monkey fish g
  • Spring Boot自动装配存储库始终为空[重复]

    这个问题在这里已经有答案了 每次我进入我的服务类时 存储库似乎都没有自动连接 因为它不断抛出 NullPointerException 谁能帮我检查一下我缺少什么吗 这是我的代码 演示应用程序 java package com exampl
  • 通往楼梯顶部的可能路径

    这是一个非常经典的问题 我听说谷歌在他们的面试中使用过这个问题 问题 制定一个递归方法 打印从楼梯底部到楼梯顶部的所有可能的独特路径 有 n 个楼梯 您一次只能走 1 步或 2 步 示例输出 如果它是一个有 3 级楼梯的楼梯 1 1 1 2
  • Integer.parseInt("0x1F60A") 以 NumberformatException 结束

    我尝试从数据库中获取长字符串内的表情符号代码 格式如下 0x1F60A 所以我可以访问代码 但它将是String 起初 我尝试通过执行以下操作来转换变量tv setText beforeEmo getEmijoByUnicode int e
  • 来自十六进制代码的 Apache POI XSSFColor

    我想将单元格的前景色设置为十六进制代码中的给定颜色 例如 当我尝试将其设置为红色时 style setFillForegroundColor new XSSFColor Color decode FF0000 getIndexed 无论我在
  • 生成的序列以 1 开头,而不是注释中设置的 1000

    我想请求一些有关 Hibernate 创建的数据库序列的帮助 我有这个注释 下面的代码 在我的实体类中 以便为合作伙伴表提供单独的序列 我希望序列以 1000 开头 因为我在部署期间使用 import sql 将测试数据插入数据库 并且我希
  • Java实现累加器类,提供Collector

    A Collector具有三种通用类型 public interface Collector
  • HashMap 值需要不可变吗?

    我知道 HashMap 中的键需要是不可变的 或者至少确保它们的哈希码 hashCode 不会改变或与另一个具有不同状态的对象发生冲突 但是 HashMap中存储的值是否需要与上面相同 为什么或者为什么不 这个想法是能够改变值 例如在其上调
  • QuerySyntaxException:无法找到类

    我正在使用 hql 生成 JunctionManagementListDto 类的实际 Java 对象 但我最终在控制台上出现以下异常 org hibernate hql internal ast QuerySyntaxException
  • Java Swing:需要一个高质量的带有复选框的开发 JTree

    我一直在寻找一个 Tree 实现 其中包含复选框 其中 当您选择一个节点时 树中的所有后继节点都会被自动选择 当您取消选择一个节点时 树中其所有后继节点都会自动取消选择 当已经选择了父节点 并且从其后继之一中删除了选择时 节点颜色将发生变化
  • 使用按钮作为列表的渲染器

    我想使用一个更复杂的渲染器 其中包含列表的多个组件 更准确地说 类似于this https stackoverflow com questions 10840498 java swing 1 6 textinput like firefox
  • 如何重新启动死线程? [复制]

    这个问题在这里已经有答案了 有哪些不同的可能性可以带来死线程回到可运行状态 如果您查看线程生命周期图像 就会发现一旦线程终止 您就无法返回到新位置 So 没有办法将死线程恢复到可运行状态 相反 您应该创建一个新的 Thread 实例
  • org.apache.commons.net.io.CopyStreamException:复制时捕获 IOException

    我正在尝试使用以下方法中的代码将在我的服务器中创建的一些文件复制到 FTP 但奇怪的是我随机地低于错误 我无法弄清楚发生了什么 Exception org apache commons net io CopyStreamException
  • 泛型、数组和 ClassCastException

    我想这里一定发生了一些我不知道的微妙事情 考虑以下 public class Foo
  • 在java中使用多个bufferedImage

    我正在 java 小程序中制作游戏 并且正在尝试优化我的代码以减少闪烁 我已经实现了双缓冲 因此我尝试使用另一个 BufferedImage 来存储不改变的游戏背景元素的图片 这是我的代码的相关部分 public class QuizApp

随机推荐