41

有效的java 说:

// 潜在的安全漏洞!

static public final Thing[] VALUES = { ... };

谁能告诉我安全漏洞是什么?

4

8 回答 8

57

声明static final public字段通常是类常量的标志。对于原始类型(整数、双精度等)和不可变类(如字符串和java.awt.Color. 对于数组,问题在于即使数组引用是常量,数组的元素仍然可以更改,并且由于它是一个字段,更改是不受保护的、不受控制的,并且通常是不受欢迎的。

为了解决这个问题,可以将数组字段的可见性限制为私有或包私有,因此在查找可疑修改时需要考虑的代码体较少。或者,通常更好的是,一起取消数组并使用“列表”或其他适当的集合类型。通过使用集合,您可以控制是否允许更新,因为所有更新都通过方法。您可以通过使用Collections.unmodifiableList(). 但请注意,即使集合是不可变的,您还必须确保其中存储的类型也是不可变的,否则对假定常量进行未经请求的更改的风险将再次出现。

于 2010-05-16T00:18:39.577 回答
46

要了解为什么这是一个潜在的安全漏洞而不仅仅是封装不良,请考虑以下示例:

public class SafeSites {
    // a trusted class with permission to create network connections
    public static final String[] ALLOWED_URLS = new String[] {
        "http://amazon.com", "http://cnn.com"};

    // this method allows untrusted code to connect to allowed sites (only)
    public static void doRequest(String url) {
        for (String allowed : ALLOWED_URLS) {
            if (url.equals(allowed)) {
                 // send a request ...
            }
        }
    }
}

public class Untrusted {
     // An untrusted class that is executed in a security sandbox.

     public void naughtyBoy() {
         SafeSites.ALLOWED_URLS[0] = "http://myporn.com";
         SafeSites.doRequest("http://myporn.com");
     }
}

如您所见,最终数组的错误使用意味着不受信任的代码可以破坏受信任的代码/沙箱试图施加的限制。在这种情况下,这显然是一个安全问题。

如果您的代码不是安全关键应用程序的一部分,那么您可以忽略此问题。但 IMO 这是一个坏主意。在未来的某个时候,您(或其他人)可能会在涉及安全性的上下文中重用您的代码。无论如何,就是作者将公共最终数组称为安全问题的原因。


Amber 在评论中说:

如果您可以以任何一种方式读取源代码和/或字节码,那么安全漏洞不会比私有漏洞多......

这不是真的。

“坏人”可以使用源代码/字节码来确定 aprivate存在并引用数组这一事实不足以破坏安全性。坏人必须将代码注入到具有使用反射所需权限的 JVM 中。此权限不适用于在(正确实现的)安全沙箱中运行的不受信任的代码。

于 2010-05-16T02:46:01.643 回答
16

请注意,非零长度数组始终是可变的,因此类具有公共静态最终数组字段或返回此类字段的访问器是错误的。 如果一个类有这样的字段或访问器,客户端将能够修改数组的内容。

-有效的 Java,第 2 版。(第 70 页)

于 2010-05-16T00:19:58.767 回答
2

外部类可以修改数组的内容,这可能不是您希望您的类的用户执行的操作(您希望他们通过方法执行此操作)。听起来作者的意思是它违反了封装,而不是安全性。

我猜有人声明这一行可能会认为其他类不能修改数组内容,因为它被标记为 final,但这不是真的,final 只会阻止你重新分配属性。

于 2010-05-16T00:23:18.313 回答
1

在此声明中,客户端可以修改 Thing[0]、Thing[1] 等(即数组中的元素)。

于 2010-05-16T00:21:49.137 回答
1

我还将添加 Joshua Bloch 在 Effective Java 3rd edition 中提出的内容。当然我们可以很容易地改变数组的值,如果它被声明为:

public static final String[] VALUES = { "a", "b" }; 

a.VALUES[0] = "changed value on index 0";
System.out.println(String.format("Result: %s", a.VALUES[0]));

我们得到Result: changed value on index 0

Joshua Bloch 建议返回数组的副本:

private static final String[] VALUES = { "a", "b" };   
public static final String[] values()
{
    return VALUES.clone();
}

所以现在当我们尝试:

a.values()[0] = "changed value on index 0";
System.out.println(String.format("Result: %s", a.values()[0]));

我们得到Result: a了,这就是我们想要实现的——它们VALUES是不可变的。

声明public static final原始值、字符串或其他不可变对象(如public static final int ERROR_CODE = 59;

于 2018-06-22T15:00:33.173 回答
0

认为它只是意味着整个公共与私人的事情。最好将局部变量声明为私有,然后使用 get 和 set 方法,而不是直接访问它们。使它们在您的程序之外更难弄乱。据我所知。

于 2010-05-16T00:17:07.063 回答
0

因为,final 关键字只保证引用值(例如假设它是内存位置),而不保证其中的内容。

于 2010-05-16T01:44:58.017 回答