効果的Javaは言う:
//潜在的なセキュリティホール!
static public final Thing [] VALUES = {...};
誰かがセキュリティホールとは何か教えてもらえますか?
_static final public
_フィールドの宣言は、通常、クラス定数の特徴です。プリミティブ型(int、doubleなど)、および文字列や_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はこれは悪い考えです。将来のある時点で、ユーザー(または他の誰か)は、セキュリティが懸念されるコンテキストでコードを再利用する可能性があります。いずれにせよ、thisは、著者がパブリックファイナルアレイをセキュリティ上の問題と呼ぶ理由です。
琥珀はコメントでこれを言った:
ソースコードやバイトコードをどちらの方法でも読み取ることができれば、プライベート以上のセキュリティホールはありません...
本当じゃない。
「悪者」がソースコード/バイトコードを使用してprivate
が存在し、配列を参照していると判断できるという事実は不十分でセキュリティを破壊することです。悪者またはリフレクションを使用するために必要な権限を持つJVMにコードを挿入する必要があります。この権限は、(適切に実装された)セキュリティサンドボックスで実行されているuntrustedコードには使用できません。
長さが0以外の配列は常に変更可能であるため、クラスがpublic static final配列フィールド、またはそのようなフィールドを返すアクセサーを持つことは間違っていることに注意してください。クラスにはそのようなフィールドまたはアクセサーがあり、クライアントは配列の内容を変更できます。
-有効なJava、第2版(70ページ)
外部クラスは配列の内容を変更できますが、これはおそらくクラスのユーザーに実行させたいことではありません(メソッドを介して実行させたい)。作者は、セキュリティではなくカプセル化に違反していることを意味しているようです。
この行を宣言している人は、finalとしてマークされているため、他のクラスは配列の内容を変更できないと思うかもしれませんが、これは正しくありません。finalは、属性の再割り当てを停止するだけです。
この宣言では、クライアントはThing [0]、Thing [1]など(つまり、配列内の要素)を変更できます。
それは単に全体の公的対私的なことを意味すると考えてください。ローカル変数をプライベートに宣言し、直接アクセスするのではなく、getおよびsetメソッドを使用することをお勧めします。プログラムの外部をいじるのが少し難しくなります。私の知る限りそれについて。
JoshuaBlochが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;
のような他の不変オブジェクトの宣言にも問題はありません。
なぜなら、finalキーワードは参照値のみを保証し(たとえば、メモリの場所として想定)、その中のコンテンツは保証しないからです。