Externalizable インタフェースを実装するクラスは、readExternal() メソッドと writeExternal() メソッドを提供しなければならない。これらのメソッドはパッケージプライベートあるいは public であり、信頼できるコードからも、信頼できないコードからも、同じように呼び出される可能性がある。そのため、これらのメソッドが、意図した場合のみ実行され、プログラム実行中の勝手なタイミングでオブジェクトの内部状態を変更することがないようにしなければならない。
違反コード
以下の違反コード例では、readExternal() メソッドが必要上やむをえず public 宣言されており、悪意を持った呼出しが行われる場合に備えた対策が行われていないため、このメソッドを呼び出すことで、誰でもオブジェクトが持つ値をリセットできてしまう。
public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException { // フィールド値を読み込む this.name = (String) in.readObject(); this.UID = in.readInt(); //... }
適合コード
以下の適合コードでは、インスタンスフィールドの初期化が完了したら true に設定されるブール型の変数を使うことで、初期化が複数回行われないようにしている。このコードでは、private 宣言されたロックオブジェクトを使って同期することで、競合状態の発生も防いでいる(「LCK00-J. 信頼できないコードから使用されるクラスを同期するにはprivate finalロックオブジェクトを使用する」を参照)。
private final Object lock = new Object(); private boolean initialized = false; public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException { synchronized (lock) { if (!initialized) { // フィールド値を読み込む this.name = (String) in.readObject(); this.UID = in.readInt(); //... initialized = true; } else { throw new IllegalStateException(); } } }
なお、このコードをセンシティブなデータの保護に使用するのは不適切である。
リスク評価
Externalizable インタフェースを実装したオブジェクトが上書きされると、オブジェクトの内部状態が矛盾した状態になってしまう危険がある。
ルール | 深刻度 | 可能性 | 修正コスト | 優先度 | レベル |
---|---|---|---|---|---|
SER11-J | 低 | 中 | 低 | P6 | L2 |
参考文献
[API 2006] | |
[Sun 2006] | Serialization Specification, A.7, Preventing Overwriting of Externalizable Objects |
翻訳元
これは以下のページを翻訳したものです。
SER11-J. Prevent overwriting of externalizable objects (revision 47)