繁体   English   中英

为什么公共静态最终数组是安全漏洞?

[英]Why are public static final array a security hole?

有效的java说:

//潜在的安全漏洞!

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

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

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

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

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

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这是一个坏主意。 在将来的某个时刻,您(或其他人)可能会在安全性受到关注的环境中重用您的代码。 无论如何, 就是作者将公共最终数组称为安全问题的原因。


安伯在评论中说:

如果你能以任何方式阅读源代码和/或字节码,那么就不会有私有的安全漏洞......

这不是真的。

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

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

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

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

我想有人声明这一行可能会认为其他类不能修改数组内容,因为它被标记为final,但事实并非如此,最终只会阻止你重新分配属性。

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

认为它只意味着整个公共事物和私人事物。 将局部变量声明为私有,然后使用get和set方法,而不是直接访问它们应该是一种好习惯。 让他们更难以在你的程序之外搞砸。 据我所知,关于它。

因为,final关键字仅保证参考值(例如,假设它作为内存位置),但不保证其中的内容。

我还要补充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;也没什么不好的public static final int ERROR_CODE = 59;

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM