java的内部类会造成安全风险吗?

时间:2009-12-20 10:32:32

标签: java security inner-classes

最近,我项目的安全团队发布了一个安全的代码指南文档,旨在用作我们的代码审查的一部分。让我印象深刻的第一件事是“不要使用内部课程”。我认为这似乎是一个非常沉重和彻底的声明。如果正确使用,内部类很好吗?但我做了一些谷歌搜索并找到了this,为方便起见引用了这里。

  

规则5:不要使用内部类

     

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

     

但等等,情况变得更糟。内心的   class可以访问。的字段   封闭外类,即使这些   字段被声明为私有。而且   内部类被翻译成一个   单独的课程。为了这个   单独的类访问字段   外部类,编译器默默地   将这些字段从私有更改为   包装范围!这太糟糕了   内在的暴露,但它是   更糟糕的是编译器   默默地推翻你的决定   使一些领域私有化。不要用   内部课程,如果你能帮助它。   (具有讽刺意味的是,新的Java 2   doPrivileged()API使用指南   建议你使用内部类   写特权代码。那是一个   我们不喜欢的原因   doPrivileged()API。)

我的问题是

  1. 这种行为在java 5/6中是否仍然存在?
  2. 这是否真的存在安全风险,因为除了外部类和内部类之外,任何试图访问外部类的私有成员的类都不会编译?
  3. 是否有足够的安全风险来放弃“指南”“不要使用内部类”?

10 个答案:

答案 0 :(得分:18)

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

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

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

答案 1 :(得分:9)

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

答案 2 :(得分:9)

  

这种行为在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在类文件中的含义)

答案 3 :(得分:6)

  

这种行为在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方法。

答案 4 :(得分:4)

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

如果您正在分发已编译的代码而不对其进行模糊处理,那么如果您担心人们修改您的私有部分,那么使用内部课程是您最后的担忧。

如果您正在托管您的代码,那么您为什么担心有人会在您的内部课程中徘徊?

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

就像我上面所说,如果这是贵公司的政策,请及时向贵公司报告thedailywtf.com

答案 5 :(得分:4)

您应该考虑应用程序必须提供哪种安全性。具有安全体系结构的应用程序不会遇到这些命名问题。

如果某些用户不允许使用您的代码,则必须单独使用此功能并在服务器上运行(用户无法访问类文件)。

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

答案 6 :(得分:4)

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

我的个人意见是,不被其他可能性所淹没的原因,所以如果你需要它,它是有意义的,它是可读的,使用内部类。

答案 7 :(得分:3)

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

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

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

答案 8 :(得分:-1)

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

因此,如果这条规则可以帮助您的公司,那么可能会想到获得静态内部类,因为它们提供了一种封装方式,这在很多情况下很有用。

@Tom,引用Java language specification,“会员类可能静态,在这种情况下,他们对周围班级的实例变量无法访问

答案 9 :(得分:-1)

无义!遵循相同的逻辑,也不要写公共方法,因为他们可以访问私有字段,gush!