【问题标题】:Do java's Inner classes pose a security risk?java的内部类会带来安全风险吗?
【发布时间】:2010-12-28 11:22:31
【问题描述】:

最近,我项目的安全团队发布了一份安全代码指南文档,旨在用作我们代码审查的一部分。首先让我印象深刻的是一个项目,上面写着“不要使用内部类”。我认为这似乎是一个非常严厉和笼统的声明。如果使用正确,内部类很好吗?但我做了一些谷歌搜索,发现this,为方便起见,在此引用。

规则 5:不要使用内部类

一些 Java 语言书籍这样说 内部类只能通过 包含它们的外部类。 这不是真的。 Java字节码有 没有内部类的概念,所以内部 类由编译器翻译 进入碰巧的普通班级 可以访问同一代码中的任何代码 包裹。规则 4 说不要依赖 关于包的保护范围。

但是等等,情况会变得更糟。一个内 类可以访问 封闭外部类,即使这些 字段被声明为私有的。和 内部类被翻译成 单独的班级。为了允许这 单独的类访问字段 外部类,编译器静默 将这些字段从私有更改为 包范围!已经够糟糕了 内部类是暴露的,但它是 更糟糕的是编译器是 默默地推翻你的决定 将某些字段设为私有。不要使用 内部类,如果你能提供帮助的话。 (具有讽刺意味的是,新的 Java 2 doPrivileged() API 使用指南 建议您使用内部类 编写特权代码。那是一个 我们不喜欢的原因 doPrivileged() API。)

我的问题是

  1. 这种行为在 java 5 / 6 中是否仍然存在?
  2. 考虑到除了外部类和内部类之外的任何试图访问外部类的私有成员的类都无法编译,这实际上是否存在安全风险?
  3. 是否构成足够的安全风险来警告“准则”“不要使用内部类”?

【问题讨论】:

  • 等一下?这是否意味着“我不能使用最流行的方式在 Swing 中创建回调?”
  • 如果这确实是贵公司在代码审查期间的安全政策,请及时向dailywtf.com报告贵公司
  • @Zac Bowling:thedailywtf.com,另一个只是一个门口页面。
  • Zac Bowling:特别是 JDK 充满了内部类(正如引用中提到的那样)。
  • 代码可见性从未成为一项安全功能。甚至可以使用反射访问私有成员。

标签: java security inner-classes


【解决方案1】:
  1. 是的,这种行为仍然存在。
  2. 这是一个安全风险,因为流氓类可以使用标准 javac 以外的其他东西制作。
  3. 这取决于你有多偏执:) 如果你不允许外星类在你的 JVM 中运行,我看不出有什么问题。如果你这样做了,你就会遇到更大的问题(沙盒和所有问题)
  4. 我知道你只有 3 个问题,但和这里的其他人一样,我认为这是一个愚蠢的限制。

【讨论】:

  • 我要强调 3。如果肇事者进入你的机器/VM,你已经迷路了,再多的“安全编码”也无法解决这个问题。
  • Esko:如果您运行未签名的小程序,您是否会失败?我认为没有!
  • @Esko 除非您准备好适当的安全模型来处理它,并且您可以控制外来代码的启动方式。
【解决方案2】:

这种代码安全性的想法有点愚蠢。如果您想要代码级别的安全性,请使用混淆工具。就像@skaffman 在上面的 cmets 中所说,“代码可见性从来都不是安全功能。即使是私有成员也可以使用反射访问。”。

如果您要分发编译后的代码而不是对其进行混淆处理,那么如果您担心有人修改您的隐私,那么使用内部类是您最后的担心。

如果您要托管您的代码,那么您为什么要担心有人在您的内部类周围探查?

如果您要链接一些您不信任且无法在运行时检查的第 3 方代码,则将其沙箱化。

如我上面所说,如果这确实是贵公司的政策,请及时将贵公司报告给thedailywtf.com

【讨论】:

  • 我不明白你的评论:mR_fr0g 公司的安全人员不希望私有字段公开,无论以何种方式,无论是否现实。混淆工具不会改变任何级别的安全性,并且所有获取私有字段访问权限的方法在混淆后仍然有效。
  • 是的,这是个糟糕的建议。混淆不会让任何事情变得更安全,它只会意味着黑客需要做更多的工作。但最终他们仍然可以做之前可以做的任何事情。
  • 你可以通过反射访问私有字段
  • 仅供参考,如果您没有 ReflectPermission("suppressAccessChecks") 权限,则无法通过反射访问私有字段。
  • (Jerome:很好。好吧,除非你在那个班级里进行反思,这几乎没有意义。)
【解决方案3】:

您应该考虑您的应用程序必须提供什么样的安全性。具有安全架构的应用程序不会遇到这些命名问题。

如果不允许用户使用您的代码做某事,您必须分离此功能并在服务器上运行它(用户无权访问类文件)。

请记住,您始终可以反编译 java 类文件。并且不要依赖“默默无闻的安全”。即使是混淆的代码也可以被分析、理解和修改。

【讨论】:

    【解决方案4】:

    恶意代码可以使用 java 反射来获取 JVM 中的任何信息,除非有安全管理器禁止这样做,这包括将私有字段更改为公共字段等等。

    我个人的看法是,不这样做的原因被其他可能性压倒了,所以如果你需要它,它是有意义的,并且它是可读的,使用内部类。

    【讨论】:

      【解决方案5】:

      请注意,列出的缺点不适用于static 内部类,因为它们没有对其封闭类(或真正的对象)的隐式访问。

      因此,如果这条规则对您的公司有所帮助,那么将静态内部类排除在外可能是一个好主意,因为它们提供了一种在许多情况下都很有用的封装方式。

      @Tom,引用Java language specification,“成员类可能是静态的,在这种情况下,它们无法访问周围类的实例变量”

      【讨论】:

      • 静态嵌套类拥有对封闭类的私有访问权限(反之亦然)。
      【解决方案6】:

      此信息已经过时了大约十年。带有AccessController.doPrivileged 的匿名内部类的广泛使用应该是一个线索。 (如果您不喜欢该 API,请考虑 JDK 中错误缺失的 try-finally 块的比例。)

      政策是,如果两个类由不同的类加载器加载或具有不同的证书,则它们不能共享同一个包。为了获得更多保护,请在您的罐子清单中将包裹标记为已密封。因此,从安全的角度来看,“规则 4”是虚假的,因此也是这条规则。

      在任何情况下,制定安全策略您都应该了解您要防范的内容。这些类型的策略用于处理可能具有不同信任级别的移动代码(移动的代码)。除非您正在处理移动代码,或者您的代码正在进入可能需要的库,否则这些预防措施几乎没有意义。但是,使用健壮的编程风格几乎总是一个好主意,例如复制和验证参数和返回值。

      【讨论】:

      • 复制参数并不总是有助于使您的代码更安全,例如,当您在参数集合上调用 toArray() 时,您信任参数的 toArray() 实现!
      • Adrian:是的,如 SUn 的 Java 安全代码指南中所述。
      【解决方案7】:

      这种行为在 java 5 / 6 中是否仍然存在?

      您可以使用javap 工具来确定您的二进制文件公开的内容以及公开方式。

      package demo;
      public class SyntheticAccessors {
        private boolean encapsulatedThing;
      
        class Inner {
          void doSomething() {
            encapsulatedThing = true;
          }
        }
      }
      

      上述代码(使用 Sun Java 6 javac 编译)在 SyntheticAccessors.class 中创建了这些方法:

      Compiled from "SyntheticAccessors.java"
      public class demo.SyntheticAccessors extends java.lang.Object{
          public demo.SyntheticAccessors();
          static void access$0(demo.SyntheticAccessors, boolean);
      }
      

      注意新的access$0 方法。

      【讨论】:

        【解决方案8】:

        这种行为在 java 5 / 6 中是否仍然存在?

        与描述的不完全一样;我从未见过这样的编译器:

        为了允许这个单独的类访问外部类的字段,编译器默默地将这些字段从私有更改为包范围!

        相反,IIRC Sun Java 3/4 创建了一个访问器,而不是修改该字段。

        Sun Java 6 (javac 1.6.0_16) 创建一个静态访问器:

        public class InnerExample {
            private int field = 42; 
        
            private class InnerClass {
                public int getField () { return field; };
            }
        
            private InnerClass getInner () { 
                return new InnerClass();
            }
        
            public static void main (String...args) {
                System.out.println(new InnerExample().getInner().getField());
            }
        }
        
        
        $ javap -classpath bin -private InnerExample
        Compiled from "InnerExample.java"
        public class InnerExample extends java.lang.Object{
            private int field;
            public InnerExample();
            private InnerExample$InnerClass getInner();
            public static void main(java.lang.String[]);
            static int access$000(InnerExample);
        }
        
        
        $ javap -classpath bin -c -private InnerExample
        static int access$000(InnerExample);
          Code:
           0:   aload_0
           1:   getfield    #1; //Field field:I
           4:   ireturn
        

        考虑到除了外部类和内部类之外的任何试图访问外部类的私有成员的类都不会编译,这实际上是否存在安全风险?

        我在这里推测了一下,但是如果您针对该类进行编译,则不会,但是如果您添加access$000,那么您可以编译使用访问器的代码。

        import java.lang.reflect.*;
        
        public class InnerThief {
            public static void main (String...args) throws Exception {
                for (Method me : InnerExample.class.getDeclaredMethods()){
                    System.out.println(me);
                    System.out.printf("%08x\n",me.getModifiers());
                }
        
                System.out.println(InnerExample.access$000(new InnerExample()));
            }
        }
        

        有趣的是,合成访问器有修饰符标志00001008,如果你添加一个包级静态方法,它有标志00000008。 JVM 规范的第二版中没有该标志值,但它似乎阻止了 javac 看到该方法。

        看来那里有一些安全功能,但我找不到任何文档。

        (因此这篇文章在 CW 中,以防有人知道 0x1000 在类文件中的含义)

        【讨论】:

        • 合成修饰符呢?
        • 我认为是,但没有找到来自 Sun 的任何参考资料,也没有任何内容说“如果一个方法具有合成修饰符,如果不先将其设置为可访问,则无法通过反射调用它” ,这将消除感知到的安全风险。
        • 奇怪的是寻找 Java 6 JVMS 会是redirected to the hopelessly outdated 2nd edition。它甚至缺少明显的 Java 5 特性、enums、注释、泛型等。根本没有直接链接到第 3 版。 Java 7 edition documents the ACC_SYNTHETIC 修饰符。在 Java 5 之前,the Synthetic attribute 用于相同目的。
        【解决方案9】:

        “考虑到除了外部类和内部类之外,任何试图访问外部类的私有成员的类都不会编译,这实际上是一种安全风险吗?”

        即使在正常情况下无法编译,您仍然可以生成自己的字节码。但这不是避免内部类的理由。您所要做的就是假设您所有的内部类都是公共的。

        如果您真的希望能够运行不受信任的代码,请了解如何使用 The Java Security Architecture 设置您自己的沙箱和安全级别,这并不难。但大多数情况下,您应该避免在安全的环境中运行随机代码。

        【讨论】:

          【解决方案10】:

          废话!遵循同样的逻辑,也不要编写公共方法,因为它们可以访问私有字段,gush!

          【讨论】:

          • 这不是语言问题。嵌套类可以访问封闭类中的私有,反之亦然,这不是正在讨论的问题。 (非)问题是添加嵌套类可能导致注入同一包的任何代码都可以访问私有代码(如果实际上可以将恶意代码注入包中)。
          猜你喜欢
          • 2016-07-02
          • 2012-08-19
          • 2014-11-24
          • 1970-01-01
          • 2013-05-03
          • 1970-01-01
          • 2017-09-24
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多