为什么Java安全编码很重要?

6

我对为什么要进行Java安全编码感到困惑。例如,为什么声明变量为私有很重要?我的意思是,我明白这会使得从类外部访问这些变量变得不可能,但我可以简单地反编译该类以获取值。 同样,将类定义为final将使得无法对该类进行子类化。何时对一个类进行子类化会对安全造成危险?同样,如果需要,我可以反编译原始类并使用任何恶意代码重新实现它。 问题出在应用程序被用户“信任”时吗?人们能否以某种方式滥用这种信任? 基本上,我正在寻找一些好的例子来说明为什么应遵循安全编码准则。

7个回答

15

编程很难。

如果您定义了严格的API,并且不公开不应公开的变量(我们称之为封装),则可以帮助API的用户,从而使编程更容易。这被认为是一件好事。

原因主要不是“安全”,即保守秘密事项,而是清晰、简单和易于理解。

当然,作为额外的好处,如果您知道API的用户没有在背后更改“您”的变量,则更容易使事情正常工作。


1
为什么封装总是以安全性为基础来解释呢?此外,为什么封装会使编程更容易? - JDelage

4
这段内容的意思是,"secure"指的是类内部的工作对于使用它的人来说是隐藏的。这里的"secure"并不是指"保护服务器",而是指一个类的用户不需要担心它如何执行他想要的任务。
举个例子,如果暴露一个类的变量,那么这个类的用户就会知道这些变量的存在,但你并不希望这样,就像按下一个按钮打开灯一样,你不需要知道里面有什么材料或者其他东西来完成这个任务。

有趣!终于在这个背景下给出了一个好的“安全”解释。 - Jackson

4

Java是一种面向对象的编程语言,而面向对象编程的关键概念之一是封装

封装的思想是“隐藏”实现细节,例如保存对象状态的内部变量和算法等内部工作,并仅提供其他对象可以使用的接口来执行对象功能。

使用该概念,人们希望通过使用private变量来隐藏内部状态,以防止其他对象直接影响内部状态。在Java中,通常会看到getter和setter(例如getColorsetColor)来处理对象。

此外,封装还可以增加代码的鲁棒性。

例如,通过限制对内部状态的访问,可以在更改对象之前执行某些合理性检查。

作为一个实例,假设有一个 Score 对象,它应该具有介于 0 和 100 之间的百分比值。通过提供一个 setPercent(int) 方法来验证指定值是否在允许范围内,可以防止 Score 对象被设置为不可接受的状态。因此,如果 setPercent 方法导致错误或抛出异常,则可以防止直接通过编写类似于 score.percent = 150 的语句来操纵内部状态。

3
这里有两个问题。
首先,当将变量声明为protected或private时,它们不会成为您的公共API的一部分。未来其他类可能依赖于您的类,如果您想包含新功能、提高性能等,重要的是您可以自由更改尽可能多的内容。如果所有值都是public,则所有内部值和机制也都是public。更改它们可能会破坏依赖于您的类的其他类。
其次,暴露变量会允许其他类更改您的值。如果它们更改了您的内部值,则可能会破坏您的程序并创建奇怪的意外行为。如果您创建一个依赖于某个类的准确性能的系统,并且内部值被更改,则您不能再依赖该系统。子类化使这更加复杂。您的系统可能依赖于某种类型的类执行预期的操作。通过子类化,可以创建一个新类,它看起来是相同类型的,但不执行预期的操作。
例如,如果您有一个名为square的类,其中包含一个受保护的函数getArea(),您希望它返回正方形的面积。但是,可以创建一个扩展square的新类,比如class rectangle extends square。现在,rectange可以覆盖getArea(),但它仍然是square类型,这可能会破坏依赖于square功能的某些内容。通过将类设置为final,您断言在您的系统中永远不会发生这种情况。
这种“安全编码”类型无法防止其他人查看您的源代码,但有助于使您的代码在未来更可靠和可用。

受保护的变量是公共API的一部分。请使用私有变量,或者如果必要的话,使用一些“包私有”默认访问变量。 - Tom Hawtin - tackline

1
只是为了补充其他人已经说过的:其中一些特性也可以简单地视为表达意图的方式。如果我将成员设为private,我就使得其他人“无法”访问它(虽然这是可能的,但这不是重点),更重要的是,我告诉用户这是一个实现细节,他们不应该依赖它。

1

想象一下,如果您的对象具有不是私有(隐藏)的内部属性,并且运行代码访问此属性的多线程环境中,N个线程将同时开始访问它,其中5个线程想要更改此属性,4个线程想要读取。没有办法确保事情会顺利进行,也没有线程知道它在这一刻持有哪些数据以及是否成功更改了该对象的属性。

您将不得不编写特殊的代码片段,负责处理同步访问,但仍然不能保证您的代码将正常工作,因为您仍然必须检查程序中其余680个类是否直接访问该属性。

简而言之,您面临着巨大的问题,调试是一场噩梦,因为您不知道数据何时被更改,哪个线程进行了更改,从哪里发生等等。

这只是不封装时发生的一个场景...

好消息是,您的代码运行速度快了1%,堆栈上的负载较小,您可能获得了可以忽略不计的性能提升,但您将付出系统定期崩溃和成功调试的微小机会的代价。


1

「安全编码」一词指的是构建软件时明确尝试回避安全漏洞的过程,无论是在 C、Java、Ruby、汇编语言或其他任何语言中。也许最核心的部分,在选择安全的语言系统之后,就是要坚持良好的编程实践。如果程序不清晰,那么它被认为是值得信任的机会就很小了。

Java 有两个著名的指南:

在Java中,有两种不同的安全编码模式。

其中一种是处理可能没有您代码所有权限的代码。例如,如果您正在编写库或签署代码,则需要这样做。恶意代码不应该能够以意外的方式利用您的权限。这很困难!

更常见的情况是处理仅涉及不受信任数据的程序。例如,Web服务器(考虑XSS和SQL注入)和处理不受信任文件的桌面应用程序(通常问题出现在具有缓冲区溢出的C代码上 - 真正的C ++更好)。在某些情况下,拒绝服务(DoS)可能是一个严重问题。

有一些重叠。例如,解释器以解释器代码的权限运行,可能非常“强大”。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接