21

虽然在 Java 中使用“final”关键字的原因有很多,但我一遍又一遍地听到的其中一个原因是它使您的代码更安全。虽然在这个微不足道的情况下这似乎是有道理的:

public class Password
{
    public final String passwordHash;
    ...
}

使用 final 关键字,您会认为没有恶意代码能够更改变量 passwordHash。但是,使用反射可以更改 passwordHash 字段的最终修饰符。

那么“最终”是否提供任何真正的安全性,还是只是安慰剂?

编辑: 有一些非常有趣的讨论,我希望我能接受多个答案。谢谢各位的意见。

4

12 回答 12

39

这不是“抵御攻击”意义上的“安全”;这更像是“更难搞错”。

我更喜欢“安全”这个词,因为我觉得这更像是防止事故,而不是恶意。

于 2010-01-21T18:40:27.117 回答
11

Java 的final关键字不用于这种安全性。它不能替代通常需要加密解决方案的东西。

在这类讨论中,“安全性”通常指的是安全对象模型的概念——也就是说,一个对象模型不能被消费者操纵,以达到类的原始作者无意的目的。

于 2010-01-21T18:37:35.607 回答
8

我不确定我是否会依靠语言结构来增加系统的安全性。

我认为进行决赛不会增加针对恶意攻击的安全性(更有可能针对错误,当然还有线程问题)。唯一的“真实形式”的安全性是,如果您有一个最终常量字段,它可能会在编译时内联,因此在运行时更改其值不会产生任何影响。

我在继承的上下文中更多地听说过 final 和 security 。通过将类设为 final,您可以防止某人对其进行子类化并触摸或覆盖其受保护的成员,但我再次使用它来避免错误而不是防止威胁。

于 2010-01-21T18:41:07.747 回答
4

一般来说,final、private 和其他此类构造应该更多地被视为一般的偏好声明,而不是严格强制执行的安全性。

但是,如果您控制进程正在运行的 JVM(例如,您在 JVM 上运行其他人提供的代码),那么 final 和 private 确实提供了安全性——再加上 Java 的 SecurityManager。可以防止通过反射来绕过这些限制的事情。

你不能做的是发布代码以在其他人的 JVM 上运行,并认为你以这种方式隐藏任何东西。

编辑:汤姆提醒我,序列化攻击(即故意提供坏的序列化数据二进制流)也可以通过正确使用最终字段来部分防止。Effective Java 有更多这样的例子。

于 2010-01-21T18:44:34.933 回答
4

防范什么?

就移动代码(可以在系统之间移动的代码——applet、midlets、WebStart、RMI/JINI 等)而言,它非常重要。应用于类,以及在较小程度上可访问的方法,它可以防止恶意实现。与可访问字段类似,值得注意的静态。如果您要编写可能成为库一部分的代码,您需要敏锐地意识到这一点。

例如,对于典型的 Web 或桌面应用程序代码,它的重要性要小得多。然而,它在字段上的缺失使代码更难阅读,并表明程序员很困惑。这样的代码不太可能仅仅因为写得不好而安全。

于 2010-01-21T18:49:19.880 回答
3

它不会使您的代码更安全,它比其他任何东西都更适合线程安全。如果一个变量被标记为final,则必须在创建对象时为其分配一个值。创建对象后,不能使该变量引用另一个值。

此行为允许您推断对象的状态并在多个线程同时访问它时做出某些假设。

于 2010-01-21T18:38:32.160 回答
2

我想当有人说final使您的代码更安全时,他们的意思是它可以防止未来的开发人员出现并修改不打算修改的值,或者从非设计为扩展的类继承(并导致不可预测的过程中的结果)。它与身份验证没有任何关系(直接)。

于 2010-01-21T18:39:10.983 回答
2

我很确定 final 是一个设计结构,与访问修饰符在类声明中的方式几乎相同——它是一种表达和强制设计的方式。

于 2010-01-21T19:19:22.640 回答
1

它更多的是关于“改变”东西而不是“保护”。最后的关键字只是放弃了更改/修改/扩展任何方法的能力。

于 2010-01-21T18:37:40.483 回答
1

Final 还改进了性能/内存管理。

于 2010-01-21T18:40:48.267 回答
1

“final”关键字确实有一些安全隐患。想象一下,您正在设计一个安全系统,该系统具有一个服务,给定一个字符串,当且仅当该字符串有效时才执行某些操作。你可能会写:

public void doSomethingUseful(String argument) {
    checkValidity(argument);
    /* prepare to do something useful... preparation takes a bit of time! */
    reallyDoTheUsefulTask(argument);
}

如果 String 不是最终的,一些聪明的攻击者可以继承 String。他们的字符串不像股票 String 类那样不可变 - 事实上,他们可以产生另一个线程并尝试通过在 checkValidity 之后但在您实际使用参数之前更改参数来对您的方法进行攻击。然后你的“有用的任务”突然做了一些完全错误的事情,可能会危及安全。他们刚刚绕过了你的支票!但是,因为 java.lang.String 是最终的,所以您可以很好地保证,当您请求 String 参数时,它实际上是标准的不可变 String。这是一个相当大的问题 - 有一整类内核模式攻击基于对系统调用的不当参数处理。

所以是的,final 可以有一些安全考虑。

于 2010-01-21T18:47:52.353 回答
1

final 关键字通常用于保持不变性。对类或方法使用 final 是为了防止方法之间的链接被破坏。例如,假设 X 类的某个方法的实现假定方法 M 将以某种方式运行。将 X 或 M 声明为 final 将防止派生类重新定义 M 以导致 X 行为不正确。它保护对象和方法不被操纵。但是对于密码学目的,使用 final 关键字不是解决方案

于 2015-07-28T18:50:29.507 回答