问题
Effective java says:
// Potential security hole!
static public final Thing[] VALUES = { ... };
Can somebody tell me what is the security hole?
回答1:
Declaring static final public
fields is usually the hallmark of a class constant. It's perfectly fine for primitive types (ints, doubles etc..), and immutable classes, like strings and java.awt.Color
. With arrays, the problem is that even though the array reference is constant, the elements of the array can still be changed, and as it's a field, changes are unguarded, uncontrolled, and usually unwelcome.
To combat this, the visibility of the array field can be restricted to private or package private, so you have a smaller body of code to consider when looking for suspicious modification. Alternatively, and often better, is to do away with the array together and use a 'List', or other appropriate collection type. By using a collection, you control if updates are allowed, since all updates go through methods. You can prevent updates by wrapping your collection using Collections.unmodifiableList()
. But beware that even though the collection is immutable, you must also be sure that the types stored in it are also immutable, or the risk of unsolicited changes on a supposed constant will reappear.
回答2:
To understand why this is a potential security hole and not just poor encapsulation, consider the following example:
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");
}
}
As you can see, the mistaken use of a final array means that untrusted code can subvert the restriction that the trusted code / sandbox is trying to impose. In this case, this is clearly a security issue.
If your code is not part of a security critical application, then you could ignore this issue. But IMO this is a bad idea. At some point in the future you (or someone else) might reuse your code in a context where security is a concern. At any rate, this is why the author calls public final arrays a security issue.
Amber said this in a comment:
No more a security hole than private would be, if you can read the source code and/or bytecode either way...
This is not true.
The fact that a "bad guy" can use source code / bytecodes to determine that a private
exists and refers to an array is not sufficient to break security. The bad guy also has to inject code into a JVM that has the required permissions to use reflection. This permission is not available to untrusted code running in a (properly implemented) security sandbox.
回答3:
Note that a nonzero-length array is always mutable, so it is wrong for a class to have a public static final array field, or an accessor that returns such a field. If a class has such a field or accessor, clients will be able to modify the contents of the array.
-- Effective Java, 2nd Ed. (page 70)
回答4:
An outside class can modify the contents of the array, which is probably not what you want users of your class to do (you want them to do it through a method). It's sounds like the author meant it violates encapsulation, and not security.
I guess someone declaring this line could think other classes can't modify the array contents since it's marked as final, but this is not true, final only stops you from re-assigning the attribute.
回答5:
In this declaration, a client can modify Thing[0], Thing[1], etc (i.e. elements in the array).
回答6:
Think it just means the whole public vs private thing. It's supposed to be good practice to have local variables declared private, then using get and set methods, rather than accessing them directly. Makes them a little harder to mess with outside of your program. About it as far as I'm aware.
回答7:
Because, final keyword assures only reference value (assume it as memory location for example) but not the content in it.
回答8:
I would also add what Joshua Bloch proposed in Effective Java 3rd edition. Of course we can easily change the value of the array if it is declared as:
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]));
and we get Result: changed value on index 0
Joshua Bloch proposed to return copy of array:
private static final String[] VALUES = { "a", "b" };
public static final String[] values()
{
return VALUES.clone();
}
so now when we try:
a.values()[0] = "changed value on index 0";
System.out.println(String.format("Result: %s", a.values()[0]));
we get Result: a
and that's what we wanted to achieve - the VALUES
are immutable.
There is also nothing bad in declaring public static final
a primitives values, Strings or other immutable objects like public static final int ERROR_CODE = 59;
来源:https://stackoverflow.com/questions/2842169/why-are-public-static-final-array-a-security-hole