ディフェンシブコピー
この場合の定石は、「ディフェンシブコピー」を行うこと、つまり、渡されたインスタンスの複製を作って使うことです。Javaセキュアコーディングスタンダードでは、OBJ06-Jというルールでこれを説明しています。
ディフェンシブコピーを行うように更新したコンストラクタを次に示します。
public User(i_Container arg){
ic = new i_Container(arg.get_i());
}
引数で受け取ったi_Containerインスタンスのフィールドiの値をget_i()メソッドで求め、それを使って改めてi_Containerインスタンスを生成します。こうすることで、外部からこのインスタンスを操作される危険がなくなります。
全く同じことが、クラス内部で使っている参照を返り値として返す場合にも当てはまります。
Userクラスのget_ic()メソッドはicの値(参照)をそのまま返しています。これを受け取ったコードでは、icが指すインスタンスが持っている値を変更することが可能です。こちらにもディフェンシブコピーを適用すべきでした。get_ic()メソッドもディフェンシブコピーを行うように更新したUserクラスのコードを次に示します。
class User {
private i_Container ic;
public User(i_Container arg){
ic = new i_Container(arg.get_i());
}
public i_Container get_ic(){
return new i_Container(ic.get_i());
}
// その他のメソッド
}
Javaセキュアコーディングスタンダードでは、OBJ05-Jというルールがこれを説明しています。
コピーコンストラクタと静的ファクトリメソッド
前述のi_Containerクラスでは、フィールドiの値だけ分かれば簡単にインスタンスの複製を作ることができました。しかし、より複雑で大きなクラスを扱う場合、今回のようにメンバフィールドの複製を作る作業は繁雑になってきます。しかもインスタンスの複製を作る作業はクラスの内部構造にも依存するので、複製を作るコードがそのクラスの外部にあるのはカプセル化の観点からも好ましいことではありません。
そこで、インスタンスの複製が必要となることが想定されるクラスは、複製をつくるメソッドを提供すべきと考えられます。このようなときに使われるのがコピーコンストラクタや静的ファクトリメソッドです。これらについては、JavaセキュアコーディングスタンダードのOBJ04-Jを参照してください。
まとめ
今回はJavaの参照型についてとりあげ、参照型データを扱うときに重要となるディフェンシブコピーの考え方について紹介しました。みなさんが今までに書いたJavaアプリケーションには、このようなディフェンシブコピーが必要な部分がなかったでしょうか? いちど見直してみることをおすすめします。
JDK 7の初期のバージョンにバンドルされていたライブラリのなかでも、このようなディフェンシブコピーを行っていなかった例が発見されています。そのコードは次のようなものでした。
public class InvalidityDateExtension ...... {
......
private Date date;
......
public Object get(String name){
if (name.equalsIgnoreCase(DATE)){
return date;
} else { ... }
}
......
}
InvalidityDateExtensionクラスのdateフィールドはprivate宣言されています。しかし、get()メソッドではdateフィールドの値をディフェンシブコピーすることなくそのまま呼出し元に返しているため、この返り値を使えばdateフィールドが参照しているDateインスタンスを改変することが可能でした。この部分のコードはOpenJDKのソースでは以下のファイルに収められています。
openjdk/jdk/src/share/classes/sun/security/x509/InvalidityDateExtension.java
OpenJDKのソースコードを参照できる方は、上記の部分が現在のコードでどのように修正されているか、確認してみてください。
参考文献
- OBJ06-J. 可変入力や可変な内部コンポーネントはディフェンシブコピーを作成する
- OBJ05-J. privateかつ可変なクラスメンバへの参照を返す前にそのディフェンシブコピーを作成する
- OBJ04-J. 信頼できないコードにインスタンスを安全に渡すため、可変クラスにはコピー機能を実装する
- 『Java言語仕様 第3版』(第4章 型, 値, 変数)、James Gosling・Guy Steele・Bill Joy・Gilad Bracha 原著、村上雅章 訳、ピアソン・エデュケーション、2006年12月
- "Defective Java Code: Mistakes That Matter"
