Java’final’关键字是否真正提高了安全性?

虽然有很多理由在Java中使用’final’关键字 ,但我一遍又一遍地听到的一个原因是它使您的代码更安全。 虽然这在这个简单的案例中似乎有意义:

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

使用final关键字,您可以预期没有恶意代码可以更改变量passwordHash。 但是, 使用reflection可以在passwordHash字段上更改最终修饰符。

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

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

在“经受住攻击”的意义上,这不是“安全”; 它更像是“更难搞乱”。

我更喜欢“安全”这个词; 我觉得它更像是防止意外,而不是恶意。

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

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

我不确定我会依赖语言结构来增强系统的安全性。

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

我在inheritance的背景下听说过最终和安全性。 通过使一个类最终你可以阻止某人inheritance它并触摸或覆盖其受保护的成员,但我再次使用它来避免错误而不是防止威胁。

对抗什么?

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

对于典型的,例如,网络或桌面应用程序代码,它远没那么重要。 但是,它在字段上的缺失使得代码更难以阅读并且表明程序员混乱。 这样的代码不太可能是安全的,因为它编写得很糟糕。

一般而言,最终,私人和其他此类结构应更多地被视为一般性偏好陈述,而不是严格执行的安全性。

但是,如果您控制JVM正在运行该进程(比如您运行JVM上其他人提供的代码),那么final和private确实提供了安全性 – 再加上Java的SecurityManager。 你可以通过反思来解决这些限制。

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

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

它不会使您的代码更安全,它更多地用于线程安全而不是其他任何东西。 如果变量标记为final,则必须在创建对象时为其分配值。 创建对象后,无法使该变量引用另一个值。

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

我想当有人说final使你的代码更安全时会有什么意思,它会阻止未来的开发人员出现并修改那些不打算修改的值,或inheritance自那些不是为了扩展(并且导致这个过程中的不可预测的结果)。 它与认证没有任何关系(直接)。

我非常确定final是一个设计构造,与访问修饰符在类声明中的方式大致相同 – 它是表达和实施设计的一种方式。

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

Final还可以改善性能/内存管理。

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

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

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

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

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