Java的内部类会带来安全风险吗?

2022-09-01 09:38:04

最近,我的项目的安全团队发布了一个安全代码指南文档,旨在用作我们代码审查的一部分。首先打动我的是一个说“不要使用内部类”的项目。我以为这似乎是一个非常严厉和笼统的声明。如果正确使用,内部类是好的吗?,但是我做了一些谷歌搜索并发现了这一,为了方便起见,这里引用。

规则 5:不使用内部类

一些Java语言书籍说,内部类只能由包围它们的外部类访问。事实并非如此。Java 字节代码没有内部类的概念,因此编译器将内部类转换为普通类,这些类恰好可供同一包中的任何代码访问。规则4说不要依赖软件包的保护范围。

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

我的问题是

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

答案 1

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

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

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


答案 2

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

不完全符合描述;我从未见过编译器是真的:

为了允许这个单独的类访问外部类的字段,编译器以静默方式将这些字段从 private 更改为包范围!

相反,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()));
    }
}

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

所以看起来那里有一些安全功能,但我找不到任何关于它的文档。

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