JPCERT コーディネーションセンター

OBJ05-J. privateかつ可変なクラスメンバへの参照を返す前にそのディフェンシブコピーを作成する

クラス内部の可変メンバへの参照を返すと、アプリケーションのセキュリティが侵害される可能性がある。カプセル化の破綻を招き、クラスの内部状態を(偶然あるいは悪意を持って)変更する機会を与えてしまうからである。したがって、プログラムは、クラスの可変メンバへの参照を返してはならない。

クラスの可変な内部状態のディフェンシブコピーへの参照を返すならば、コピーが変更可能であることに変わりはないが、呼出し元はコピー元の内部状態を変更できなくなる。

違反コード

以下の違反コードのgetDate()アクセッサメソッドは、privateDateオブジェクトのインスタンスをそのまま返している。

class MutableClass {
  private Date d;

  public MutableClass() {
    d = new Date();
  }

  public Date getDate() {
    return d;
  }
}

信頼できない呼出し元はprivateDateオブジェクトを操作できる。なぜなら、オブジェクトへの参照を呼出し元へ返すと、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クラスはprivateHashtableインスタンスフィールドを持つ。ハッシュテーブルには、不変なセンシティブデータ(社会保障番号)が格納されている。ゲッターメソッド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)

Top へ

Topへ
最新情報(RSSメーリングリストTwitter