メソッドの引数を検証し、値がメソッドの設計意図の範囲に収まることを確認すること。検証を行うことで、引数に対する処理が正しい結果を返すことが保証される。引数の検査を行わないと、間違った計算結果や実行時例外、クラスの不変条件違反、オブジェクトの内部状態に矛盾をもたらす恐れがある。
引数の検証を呼出し側(caller)と呼出される側(callee)の両端で冗長に行うディフェンシブプログラミングのスタイルは、プログラミングコミュニティの間ではあまり支持されていないが、その理由の一つはプログラムの性能が低下するからである。一般には、各インタフェースのどちらか一方で入力値検査を行うことが多い。
呼出し側で検証を行うほうが高速なコードになるが、その理由は、呼出し側が、不正な値を防ぐ条件を明確にできるからである。逆に、呼出される側で引数を検証すれば、検証コードを一か所にカプセル化することができ、コードサイズはより小さくなり、検証が一貫して正しく行われる確率が高くなる。
信頼境界(trust boundary)を越えてデータを取得するメソッドは、安全性とセキュリティの観点から、呼び出される側で引数の検証を行わなくてはならない。これはたとえばpublicなライブラリメソッドすべてに当てはまる。privateメソッドを含むその他のメソッドは、パブリックメソッドの引数に由来する信頼できない未検証の引数を検証すべきである。
ディフェンシブコピーを作成しなくてはならない場合、引数を検証する前にディフェンシブコピーを作成し、コピー元の引数ではなくコピーした引数を検証すること。詳しくは「SER06-J. 復元時には private 宣言された可変コンポーネントはディフェンシブコピーする」を参照。
違反コード
以下の違反コード例では、setState()およびuseState()で引数の検証を行っていない。悪意ある呼出し元は不正な状態をライブラリに渡すことでライブラリを破壊し、脆弱性を攻撃する。
private Object myState = null; // ライブラリの内部状態を設定する void setState(Object state) { myState = state; } // 先に渡された state を使って何らかの処理を行う void useState() { // ここで何らかの処理を行う }
内部状態がセンシティブなデータやシステムの重要データなどを参照している場合、特に深刻な脆弱性になる。
適合コード
以下の適合コードでは、メソッドの引数を検証するとともに、内部状態を、検証してから使用している。プログラムの一貫性は向上し、脆弱性が作りこまれる可能性は低減される。
private Object myState = null; // ライブラリの内部状態を設定する void setState(Object state) { if (state == null) { // state が null の場合の処理 } // state が可変なら、ここでディフェンシブコピーを作成する if (isInvalidState(state)) { // state が無効な場合の処理 } myState = state; } // 先に渡された state を使って何らかの処理を行う void useState() { if (myState == null) { // 内部状態が存在しない場合(つまり null)の処理 } // ... }
例外
MET00-EX0: 呼出し元がメソッドの引数を検証しなくてはならない旨が明記されている場合、メソッド内では引数の検証を行わなくてもよい。この場合、呼出し側はすべてのメソッド呼出しにおいて引数を検証しなくてはならない。
MET00-EX1: 引数の型が引数のとりうる状態を適切に制限している場合、引数の検証を省略することができる。このような制約はコード内にはっきりと明文化しなくてはならない。
引数の型で表現できる値のなかに、入力として有効でない値があるとしても、メソッドが正しく処理できるのであれば、この例外を適用できる。以下のコードでは、引数xとyに対して何ら明示的な検証を行っておらず、乗算の結果は正しいintではないかもしれない。このコードが安全である理由は、xとyのすべてのint値を適切に処理できるからである。
public int product(int x, int y) { long result = (long) x * y; if (result < Integer.MIN_VALUE || result > Integer.MAX_VALUE) { // エラー処理 } return (int) result; }
MET00-EX2: ごく一部の最も重要なコードを除き、たいていのコードでは、すべてのメソッド呼出しのすべての引数を検証することによって得られる効果よりも、検証処理を作り込むことによって追加されるコストと複雑性のほうが問題になるだろう。そのような場合、API(特に信頼できないコードとやり取りを行うような)の境界で引数の検証を行うことを検討すること。
リスク評価
メソッドの引数を検証しないと、一貫しない計算結果、実行時例外、制御フローに関する脆弱性等につながる恐れがある。
ルール | 深刻度 | 可能性 | 修正コスト | 優先度 | レベル |
---|---|---|---|---|---|
MET00-J | 高 | 高 | 高 | P9 | L2 |
関連ガイドライン
ISO/IEC TR 24772:2010 | Argument passing to library functions [TRJ] |
参考文献
[Bloch 2008] | Item 38: Check parameters for validity |
翻訳元
これは以下のページを翻訳したものです。
MET00-J. Validate method arguments (revision 76)