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

发布于 2024-08-15 16:13:28 字数 916 浏览 4 评论 0原文

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

规则 5:不要使用内部类

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

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

我的问题是

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

Recently the security team on my project released a secure code guidelines document, designed to be used as part of our code reviews. The first thing that struck me was an item that said "Do not use Inner classes". I thought this seemed like a very heavy handed and sweeping statement. Inner classes are good if used correctly right?, but i did a bit of googling and found this, quoted here for convenience.

Rule 5: Don't Use Inner Classes

Some Java language books say that
inner classes can only be accessed by
the outer classes that enclose them.
This is not true. Java byte code has
no concept of inner classes, so inner
classes are translated by the compiler
into ordinary classes that happen to
be accessible to any code in the same
package. And Rule 4 says not to depend
on package scope for protection.

But wait, it gets worse. An inner
class gets access to the fields of the
enclosing outer class, even if these
fields are declared private. And the
inner class is translated into a
separate class. In order to allow this
separate class access to the fields of
the outer class, the compiler silently
changes these fields from private to
package scope! It's bad enough that
the inner class is exposed, but it's
even worse that the compiler is
silently overruling your decision to
make some fields private. Don't use
inner classes if you can help it.
(Ironically, the new Java 2
doPrivileged() API usage guidelines
suggest that you use an inner class to
write privileged code. That's one
reason we don't like the
doPrivileged() API.)

My questions are

  1. Does this behaviour still exist in java 5 / 6?
  2. Is this actually a security risk, given that any class, other than the outer and inner classes, that tried to access the outer class' private members would not compile?
  3. Does it pose enough of a security risk to warant the 'guideline' 'Do not use inner classes'?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(10

要走就滚别墨迹 2024-08-22 16:13:28

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

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

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

This information is a around a decade out of date. The widespread use of anonymous inner classes with AccessController.doPrivileged should be a clue. (If you don't like the API, consider the proportion of try-finally blocks that are incorrectly missing in the JDK.)

The policy is that no two class can share the same package if they are loaded by different class loaders or have different certificates. For more protection, mark packages as sealed in the manifest of your jars. So, from a security standpoint, "Rule 4" is bogus and hence also this rule.

In any case, working out security policies you should understand what you are protecting against. These sorts of policies are for handling mobile code (code that moves) that may have different levels of trust. Unless you are handling mobile code, or your code is going into a library that may be required to, there is very little point in these sorts of precautions. However, it is almost always a good idea to use a robust programming style, for instance copying and validating arguments and return values.

乱世争霸 2024-08-22 16:13:28

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

Does this behaviour still exist in java 5 / 6?

Not exactly as described; I've never seen a compiler where this was true:

In order to allow this separate class access to the fields of the outer class, the compiler silently changes these fields from private to package scope!

Instead IIRC Sun Java 3/4 created an accessor rather than modifying the field.

Sun Java 6 (javac 1.6.0_16 ) creates a static accessor:

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

Is this actually a security risk, given that any class, other than the outer and inner classes, that tried to access the outer class' private members would not com[p]ile?

I'm speculating a bit here, but if you compile against the class it doesn't, but if you add the access$000 then you can compile code which uses the accessor.

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

The interesting thing is that the synthesised accessor has modifier flags 00001008 where if you add a package level static method it has flags 00000008. There's nothing in the second edition of the JVM spec for that flag value, but it seems to prevent the method being seen by javac.

So it appears that there's some security feature there, but I can't find any documentation on it.

(hence this post in CW, in case someone does know what 0x1000 means in a class file)

坏尐絯℡ 2024-08-22 16:13:28
  1. 是的,这种行为仍然存在。
  2. 这是一个安全风险,因为流氓类可以使用标准 javac 之外的其他内容来制作。
  3. 这取决于你有多偏执:) 如果你不允许外来类在你的 JVM 中运行,我看不出有什么问题。如果你这样做,你就会遇到更大的问题(沙箱等等)
  4. 我知道你只有 3 个问题,但像这里的其他人一样,我认为这是一个愚蠢的限制。
  1. Yes, this behavior still exists.
  2. It is a security risk because the rogue class could be crafted with something else than the standard javac.
  3. It depends of how much paranoid you are :) If you don't allow alien classes to run in your JVM, I don't see the problem though. And if you do, you have bigger problems (sandboxes and all)
  4. I know you only had 3 questions, but like other people here, I think this is a stupid restriction.
浸婚纱 2024-08-22 16:13:28

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

Does this behaviour still exist in java 5 / 6?

You can use the javap tool to determine what your binaries are exposing and how.

package demo;
public class SyntheticAccessors {
  private boolean encapsulatedThing;

  class Inner {
    void doSomething() {
      encapsulatedThing = true;
    }
  }
}

The above code (compiled with Sun Java 6 javac) creates these methods in SyntheticAccessors.class:

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

Note the new access$0 method.

眼泪也成诗 2024-08-22 16:13:28

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

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

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

You should consider what kind of security your application has to provide. An application with a secure architecture won't run into these named issues.

If there is something an user is not allowed to do with your code, you have to seperate this functionality and run it on a server (where the user has no access to the class files).

Remember that you can always decompile java class files. And don't rely on "security by obscurity". Even obfuscated code can be analyzed, understood and modified.

苍暮颜 2024-08-22 16:13:28

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

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

Malicious code can use java reflection to get to any piece of information in the JVM unless a security manager is in place which prohibits this, this includes changing private fields to public and much more.

My personal opinion is that the reasons not to, are overwhelmed by the other possibilities, so if you need it, it makes sense, and it is readable, use inner classes.

不必在意 2024-08-22 16:13:28

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

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

如果您托管代码,那么为什么担心有人窥探您的内部类呢?

如果您要链接一些您不信任且无法在运行时检查的第三方代码,请对其进行沙箱处理。

正如我上面所说,如果这确实是您公司的政策,请立即向 thedailywtf.com 报告您的公司

The idea of this kind of security in code is kind of silly. If you want code level security, use an obfuscation tool. Like @skaffman said in the comments above, "Code visibility has never been a security feature. Even private members can be accessed using reflection.".

If you are distributing your compiled code and not obfuscating it, then using an inner class is your last worry if you are worried about people tinkering with your privates.

If you are hosting your code, then why are you worried about someone poking around your inner classes?

If you going to linking some 3rd party code you don't trust and can't check at run time, then sandbox it.

Like I said above, if this is really a policy at your company, please promptly report your company to thedailywtf.com

无人接听 2024-08-22 16:13:28

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

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

如果您确实希望能够运行不受信任的代码,请了解如何使用 Java 安全架构,这并不难。但最重要的是,您应该避免在安全的环境中运行随机代码。

"Is this actually a security risk, given that any class, other than the outer and inner classes, that tried to access the outer class' private members would not compile?"

Even if it won't compile under normal circumstances, you can still generate your own bytecode. But that's no reason to avoid inner classes. All you would have to do is assume all your inner classes are public.

If you really want to be able to run untrusted code, learn how setup your own sandboxes and security levels using The Java Security Architecture, it's not that hard. But mostly, you should avoid running random code in a secure environment.

花开雨落又逢春i 2024-08-22 16:13:28

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

nonsense! follow the same logic, do not write public methods either, because they have access to private fields, gush!

只怪假的太真实 2024-08-22 16:13:28

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

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

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

Note that the drawbacks listed do not hold for static inner classes as they do not have implicit access to their enclosing class (or object really.)

So if this rule is going to help up in your company, it might be an idea to get static inner classes excempted as they offer a way for encapsulation which is useful in many cases.

@Tom, quoting the Java language specification, "Member classes may be static, in which case they have no access to the instance variables of the surrounding class"

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文