【问题标题】:why `java.lang.SecurityException: Prohibited package name: java` is required?为什么需要`java.lang.SecurityException: Prohibited package name: java`?
【发布时间】:2018-11-13 05:39:04
【问题描述】:

我创建了一个“String”类并将其放在包“java”中[实际上我想创建 java.lang 以查看哪个类被 classLoader 加载为

一旦一个类被加载到 JVM 中, 同一班(我重复一遍,同一班) 不会再次加载

引自 oreilly ] 。但是后来那件事,为什么在运行这个课程时我得到了
java.lang.SecurityException:禁止的包名:java

出于什么安全原因,java 不允许我在 java 包中有一个类?如果没有这样的检查怎么办?

【问题讨论】:

    标签: java security


    【解决方案1】:

    用户代码从不允许将类放入标准 Java 包之一。这样,用户代码就无法访问 Java 实现中的任何包私有类/方法/字段。其中一些包私有对象允许访问 JVM 内部。 (我特别想SharedSecrets。)

    【讨论】:

    • 原则上我同意对所带来风险的分析,但请注意,如果您的类位于同名包中并且由相同的 ClassLoader 加载,则只能访问包私有的东西.
    • 不,这不是原因。用户代码可以通过反射 API 访问 java 类中的私有成员。那么javax 包下的代码呢? JVM 确实允许在那里定义新的类。
    • @Rogerio:使用反射 API 访问私有内容需要 SecurityManager 检查。显然,如果您没有安装安全管理器,那么无论如何,所有的赌注都没有。
    • 永远不要说 never - 可以自己写java.lang.Object;您只需确保将其位置正确添加到引导类路径
    • @CarlosHeuberger 如果您可以在引导类路径(或认可的扩展目录)中添加一些内容,那么这将不符合“用户”代码的条件。 :-)
    【解决方案2】:

    首先,这些类型的限制用于强制执行 Java 沙箱。也就是说,在受信任的环境中运行不受信任的代码。例如,在浏览器中的计算机(受信任的环境)上从某个站点(您不一定信任)运行小程序。目的是禁止不受信任的代码访问包私有的东西,这可能有助于它逃离沙箱。

    这些限制通常由 SecurityManager 强制执行,因此当您在命令行上运行自己的应用程序时不应发生这些限制(除非您明确指定使用 SecurityManager)。当您控制环境时,您可以直接编辑 Java 的 rt.jar 中的 String.class 定义(从技术上讲,您可以,但不确定许可的含义)。正如我所说的,限制通常在 SecurityManager 中,但是这个关于 java.* 包的特殊规则在 ClassLoader 类中。

    回答您的问题:我的猜测是 java.* check 是因为 a) 历史原因 b) 在 Java 核心的某处检查类的名称,例如:所有以 java.* 开头的类都得到特殊处理。

    但是,请考虑即使您设法创建了一个名为 java.lang.String 的类,它也不会与 Java 核心定义的 java.lang.String 相同。它只是一个具有完全相同名称的类。类标识不仅仅是类的名称,尽管这可能很难理解,除非你真的在玩 ClassLoaders。

    因此,由 java.lang 包中的应用程序类加载器加载的类将无法访问核心 java.lang 包私有内容。

    为了说明这一点,尝试使用 main 方法创建一个名为 javax.swing.JButton 的类并执行它。你会得到一个java.lang.NoSuchMethodError: main。那是因为 java 在你的类之前找到了“真正的”JButton,而真正的 JButton 没有 main 方法。

    在 Java 独立应用程序中,您可以通过使用反射和 setAccessible 直接调用私有本机 defineClassx 方法之一来绕过此限制。

    顺便说一句:核心 java.lang.String 保证在您的代码执行之前被加载,因为它在任何地方都被引用,您不会首先使用您的用户代码到达那里。 JVM 在尝试加载你的类之前就已经设置好了,更不用说执行它了。

    【讨论】:

    • 这是最好的答案,但还有更多。通过“Java 代理”使用java.lang.instrument API,可以重新定义任何类加载器加载的任何类,包括引导类加载器定义的java.lang.String 类。跨度>
    • @Rogerio,以及所有其他评论员如何认为使用 -Xbootclasspath (例如,将 jsr166 用于 java.util.ForkJoinPool)?如何编译 jsr jar?
    【解决方案3】:

    你不能有“java.*”包名。这实际上是在 Java 核心中硬编码的,因此您甚至无法授予安全管理器解决它的权限(参见 ClassLoader::preDefineClass(...))

    【讨论】:

    • 更准确地说,其实你可以像'import xxx.java'一样有'java'包,而不是在开头留下'java'。
    【解决方案4】:

    java 是保留的包名。只有 JVM 内部的类才能驻留在此包中。

    如果任何人都可以在 Java 包中编写代码,那可能会导致库随意用自己的实现替换核心 Java 类。这可能会导致很多想法,从破坏核心 Java 功能到执行恶意代码。

    【讨论】:

      【解决方案5】:

      如果程序可以用木马版本覆盖 JVM 核心类,则程序可以绕过安全措施。例如,String 几乎无处不在。

      【讨论】:

        【解决方案6】:

        来自ClassLoader.defineClass(..)javadoc:

        ...指定的类名不能以“java.”开头,因为“java.*”包中的所有类只能由引导类加载器定义

        抛出:... SecurityException - 如果尝试将此类添加到包含由与此类不同的证书集签名的类的包中,或者如果类名以“java.”开头。

        【讨论】:

          【解决方案7】:

          可能在重构/补丁应用/等期间。您已在包名中添加了单词“java”,通常是包含包的文件夹。

          所以你可以以结构结束: src->main->java->java.com.yourpackage.name

          它也可能发生在测试中: src->main->test->java->java.com.yourpackage.name

          在您的 IDE 中验证它并删除“java”。部分

          【讨论】:

            【解决方案8】:

            摘自java.lang.ClassLoaderpreDefineClass方法:

            /* Determine protection domain, and check that:
                - not define java.* class,
                - signer of this class matches signers for the rest of the classes in
                  package.
            */
            private ProtectionDomain preDefineClass(String name,
                                                    ProtectionDomain pd)
            {
                ...
            
                // Note:  Checking logic in java.lang.invoke.MemberName.checkForTypeAlias
                // relies on the fact that spoofing is impossible if a class has a name
                // of the form "java.*"
                if ((name != null) && name.startsWith("java.")) {
                    throw new SecurityException
                        ("Prohibited package name: " +
                         name.substring(0, name.lastIndexOf('.')));
                }
            
                ...
            }
            

            请注意java.lang.ClassLoader 是一个抽象类,这意味着子类(例如SecureClassLoader)将实际实现它。但是,preDefineClass 方法是private,因此它不能被子类覆盖。

            preDefineClassdefineClass方法调用,即protected final。这意味着defineClass 可以被子类访问并且他们可以调用它,但是他们不能改变它的实现。

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 1970-01-01
              • 2020-08-25
              • 1970-01-01
              • 1970-01-01
              • 2012-11-21
              • 2010-10-01
              • 2011-01-09
              • 2012-06-29
              相关资源
              最近更新 更多