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

mR_*_*r0g 31 java security inner-classes

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

规则5:不要使用内部类

一些Java语言书籍说内部类只能被包含它们的外部类访问.这不是真的.Java字节代码没有内部类的概念,因此内部类由编译器转换为恰好可以被同一个包中的任何代码访问的普通类.规则4表示不依赖于包装范围进行保护.

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

我的问题是

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

Tom*_*ine 18

这些信息已经过时了十年.匿名内部类的广泛使用AccessController.doPrivileged应该是一个线索.(如果你不喜欢的API,考虑比例try- finally.那些在JDK不正确缺失块)

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

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


Jer*_*ome 9

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

  • 我强调3.如果犯罪者进入你的机器/ VM,你已经输了,没有多少"安全编码"可以解决这个问题. (7认同)
  • @Esko除非你有一个适当的安全模型准备好处理它,你控制外来代码的启动方式. (2认同)

Pet*_*ham 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
Run Code Online (Sandbox Code Playgroud)

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

我在这里推测一下,但是如果你编译它没有,但如果你添加了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()));
    }
}
Run Code Online (Sandbox Code Playgroud)

有趣的是,合成访问器具有修饰符标志00001008,如果添加包级别静态方法,则它具有标志00000008.第二版JVM规范中没有任何关于该标志值的内容,但它似乎阻止了javac看到的方法.

所以它似乎有一些安全功能,但我找不到任何文档.

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


McD*_*ell 6

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

您可以使用javap工具确定二进制文件的显示内容和方式.

package demo;
public class SyntheticAccessors {
  private boolean encapsulatedThing;

  class Inner {
    void doSomething() {
      encapsulatedThing = true;
    }
  }
}
Run Code Online (Sandbox Code Playgroud)

上面的代码(使用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);
}
Run Code Online (Sandbox Code Playgroud)

注意新access$0方法.