クラス内部の可変メンバへの参照を返すと、アプリケーションのセキュリティが侵害される可能性がある。カプセル化の破綻を招き、クラスの内部状態を(偶然あるいは悪意を持って)変更する機会を与えてしまうからである。したがって、プログラムは、クラスの可変メンバへの参照を返してはならない。
クラスの可変な内部状態のディフェンシブコピーへの参照を返すならば、コピーが変更可能であることに変わりはないが、呼出し元はコピー元の内部状態を変更できなくなる。
違反コード
以下の違反コードのgetDate()アクセッサメソッドは、privateなDateオブジェクトのインスタンスをそのまま返している。
class MutableClass {
private Date d;
public MutableClass() {
d = new Date();
}
public Date getDate() {
return d;
}
}
信頼できない呼出し元はprivateなDateオブジェクトを操作できる。なぜなら、オブジェクトへの参照を呼出し元へ返すと、MutableClassの信頼境界を越えて、クラス内部の可変要素をクラス外に公開してしまうからである。
適合コード (clone())
以下の適合コードでは、getDate()アクセッサメソッドが、Dateオブジェクトのクローンを返している。この方法が安全である理由は、Dateが攻撃者によって拡張可能である一方で、getDate()が返すDateオブジェクトはMutableClassの制御下にあり、攻撃者によって拡張可能ではないからだ。
public Date getDate() {
return (Date)d.clone();
}
クラスが信頼できないコードによってサブクラス化されうるのであれば、コンストラクタの実行中に行うディフェンシブコピーの作成にclone()メソッドを使用すべきではない。これは、悪意ある攻撃者によってオーバーライドされたclone()メソッドを呼び出さないためである。詳しくは「OBJ07-J. センシティブなクラスはコピーさせない」を参照。
クラスのフィールドを変更することが目的であるpublicなセッターメソッドを持つクラスは、「OBJ06-J. 可変入力や可変な内部コンポーネントはディフェンシブコピーを作成する」にあるアドバイスに従うこと。セッターメソッドは、内部フィールドを設定する前に、入力値検査とデータの無害化を行うことができ、また、通常そうすべきである。
違反コード (可変な配列メンバ)
以下の違反コード例では、getDateアクセッサメソッドは、Dateオブジェクトの配列を返す。このメソッドは、配列を返す前にそのディフェンシブコピーを作成していない。配列は可変なDateオブジェクトへの参照を持つため、配列の浅いコピーを作成するだけでは不十分である。攻撃者は配列のDateオブジェクトを変更できてしまう。
class MutableClass {
private Date[] date;
public MutableClass() {
date = new Date[20];
for (int i = 0; i < date.length; i++){
date[i] = new Date();
}
}
public Date[] getDate() {
return date; // あるいは date.clone() を返す
}
}
適合コード (深いコピー)
以下の適合コードでは、date配列の深いコピーを作成しそのコピーを返すことで、date配列と各Dateオブジェクトの両方を保護している。
class MutableClass {
private Date[] date;
public MutableClass() {
date = new Date[20];
for(int i = 0; i < date.length; i++) {
date[i] = new Date();
}
}
public Date[] getDate() {
Date[] dates = new Date[date.length];
for (int i = 0; i < date.length; i++) {
dates[i] = (Date) date[i].clone();
}
return dates;
}
}
違反コード (不変オブジェクトを持つ可変メンバ)
以下の違反コード例において、ReturnRefクラスはprivateなHashtableインスタンスフィールドを持つ。ハッシュテーブルには、不変なセンシティブデータ(社会保障番号)が格納されている。ゲッターメソッドgetValues()が返す参照を介し、呼出し元はハッシュテーブルにアクセスできる。信頼できない呼出し元は、ゲッターメソッドを使ってハッシュテーブルへアクセスし、テーブルのエントリの追加や削除、置き換えを不正に行うことができる。さらに、これらの変更が複数のスレッドによって実行される可能性があり、そうなると競合が発生するのは必至であろう。
class ReturnRef {
// 内部状態。センシティブなデータが含まれているかもしれない
private Hashtable<Integer,String> ht = new Hashtable<Integer,String>();
private ReturnRef() {
ht.put(1, "123-45-6666");
}
public Hashtable<Integer,String> getValues(){
return ht;
}
public static void main(String[] args) {
ReturnRef rr = new ReturnRef();
// センシティブなデータである 123-45-6666 を出力
Hashtable<Integer, String> ht1 = rr.getValues();
// 信頼できない呼び出し元はエントリを削除できる
ht1.remove(1);
// 元のエントリは削除され、null を出力
Hashtable<Integer, String> ht2 = rr.getValues();
}
}
このコード例はさらに、ハッシュテーブルhtへの参照を返しており、ガーベージコレクションを妨害している。
適合コード (浅いコピー)
privateかつ可変なオブジェクトの内部状態は、ディフェンシブコピーを作成すること。不変データを含む可変フィールドは、浅いコピーで十分である。一般に、可変データを参照するフィールドは、深いコピーを作成する必要がある。
以下の適合コードでは、不変データである社会保障番号を含むハッシュテーブルの浅いコピーを作成して返している。コピー元のハッシュテーブルはprivateに保たれ、変更されない。
class ReturnRef {
// ...
private Hashtable<Integer,String> getValues(){
return (Hashtable<Integer, String>) ht.clone(); // 浅いコピーを作成
}
public static void main(String[] args) {
ReturnRef rr = new ReturnRef();
// センシティブでないデータを出力
Hashtable<Integer,String> ht1 = rr.getValues();
// 信頼できない呼出し元はコピーに対してのみ変更を加えることができる
ht1.remove(1);
// センシティブでないデータを出力
Hashtable<Integer,String> ht2 = rr.getValues();
}
}
ハッシュテーブルにDateオブジェクトなど可変オブジェクトへの参照が含まれる場合、これらの各オブジェクトはコピーコンストラクタやメソッドを使用してコピーしなくてはならない。詳しくは「OBJ06-J. 可変入力や可変な内部コンポーネントはディフェンシブコピーを作成する」および 「OBJ04-J. 信頼できないコードにインスタンスを安全に渡すため、可変クラスにはコピー機能を実装する」を参照。
ハッシュテーブルのキーについては、深いコピーを作成する必要はなく、浅いコピーを作成するだけで十分である。ハッシュテーブルの構造上、キーは常に一貫した結果をequals()メソッドおよびhashCode()メソッドに返さなくてはならないからである。equals()メソッドやhashCode()メソッドの結果を改変できるような可変オブジェクトは、ハッシュキーには適さない。
例外
OBJ05-EX0: オブジェクトの変更が不可能なビューメソッドのみが呼び出される場合、メソッドはディフェンシブコピーを作成せず自由にビューを使用することができる。このような判断はAPIを設計する初期の段階で行うべきである。このようなメソッドを呼び出す新規の呼出し元は、変更不可能なビューのみを外部に公開すべきである。
リスク評価
オブジェクトの内部状態(可変、不変を問わず)への参照を返してしまうと、情報漏えいやオブジェクトの状態の破壊につながり、ひいてはクラスの不変条件を壊す可能性がある。場合によってはプログラムの制御フローも影響を受ける。
| ルール | 深刻度 | 可能性 | 修正コスト | 優先度 | レベル |
|---|---|---|---|---|---|
| OBJ05-J | 高 | 中 | 中 | P12 | L1 |
自動検出
適切な自動検出は不可能である。ヒューリスティックな検出は役に立つだろう。
関連する脆弱性
JDK 1.7 の初期のベータ版において、sun.security.x509.InvalidityDateExtension クラスの publicアクセッサメソッドは、ディフェンシブコピーを作成することなく Date インスタンスを返していた。Pugh は、静的解析ツール Findbugs によって発見されたこの脆弱性を [Pugh 2009] の中で引用している。
関連ガイドライン
| CERT C++ Secure Coding Standard | OOP35-CPP. Do not return references to private data. |
| MITRE CWE | CWE-375. Returning a mutable object to an untrusted caller |
参考文献
| [API 2006] | Method clone() |
| [Bloch 2008] | Item 39. Make defensive copies when needed |
| [Goetz 2006] | 3.2, Publication and Escape: Allowing Internal Mutable State to Escape |
| [Gong 2003] | 9.4, Private Object State and Object Immutability |
| [Haggar 2000] | Practical Java Praxis 64. Use clone for immutable objects when passing or receiving object references to mutable objects |
| [Security 2006] |
翻訳元
これは以下のページを翻訳したものです。
OBJ05-J. Defensively copy private mutable class members before returning their references (revision 98)



