セキュアコーディングの原則1は「信頼できない入力を全てバリデーションする」です。この原則は国際情報セキュリティ標準でも当たり前の要求事項となっています。しかし、残念ながら「信頼できない入力を全てバリデーションする」が正しく実装されているアプリケーションはほとんどありません。
独自の定義や自分勝手なセキュアコーディング解釈をしているケースが散見されます。ここでいう「セキュアコーディング」とはCERTのセキュアコーディング、ISO 27000が要求しているセキュアプログラミングを指します。
セキュアコーディングの基礎論理とホワイトリスト
セキュアコーディングは「ソフトウェアを正しく動作させる」ことを目的としています。
- ◯ ソフトウェアを正しく動作させる為に必要なコーディングを行う
- × ソフトウェアが誤作動しないようにコーディングする
前者はホワイトリスト型の手法です。後者はブラックリスト型の手法です。セキュアコーディングでは全ての入力をバリデーションし、全ての出力を安全な形で出力します。
どちらも結果は同じなりそうに思えますが、同じにはなりません。ホワイトリスト型と比べ、ブラックリスト型の手法は「仕組みとして脆弱」だからです。
例えば、マルウェア対策として以下の2つの手法のどちらがより安全でしょうか?
- 使用して良いソフトウェアを決めて使う
- 使用するとダメなソフトウェアを決めて使う
HTMLテンプレートシステムとして以下の2つの手法のどちらがより安全でしょうか?
- HTMLとして出力しなければならないモノ以外は全てエスケープする
- エスケープしなければならないモノだけエスケープする
明らかに前者の方がより安全です。前者はホワイトリスト型の手法、後者はブラックリスト型の手法です。
セキュリティ維持/向上を目的とする場合、ブラックリスト型の対策は主たる対策として利用しません。ブラックリスト型の対策は仕組みとして脆弱だからです。
既存のアプリケーションセキュリティはブラックリスト型
90年代初め「ソフトウェアにセキュリティが必要」と言われ出した当時、ソフトウェアには全くと言って良いほどセキュリティ対策がありませんでした。
セキュリティ専門家は「安全に動作するプログラムは正しく動作するプログラムである」とする論理から防御的プログラミングを提唱し、入力対策と出力対策の両方を推奨しました。
しかし、90年代は現在では古く無効となった「同じ処理のコードは一箇所にまとめて処理する」という原則が広く普及していたため、出力対策に偏重したコードでセキュリティ対策を行うことが当たり前でした。
さらに90年代ではコンピューターのCPU速度の制限から「処理速度の速さ/効率」が今以上に重要視されていました。
- 「同じ処理のコードは一箇所にまとめて処理する」
- 「処理速度の速さ/効率」の重視
これら2つの習慣から
- 必要最小限のセキュリティ対策で済ませる
という習慣が生まれました。「必要最小限のセキュリティ対策で済ませる」は「悪いと判っているモノを拒否する」という形で習慣化されました。
脆弱なブラックリスト型のセキュリティは21世紀でも猛威を振るった
最近は「悪いと判っているモノ」も一通り理解されています。しかし、全ての開発者が全ての悪いモノを一通り理解していることは期待できません。
CWE(共通脆弱性識別子)だけでも1000近く登録1されています。全ての開発者がCWE全てを理解している状態を実現することは不可能です。更にCWEだけでは十分ではなく、完璧に対応するには利用する言語/フレームワーク/ライブラリ/サブシステムなど仕様や特徴を理解する高度な知識と技術が必要です。
開発者が「悪いと判っているモノ」だけに対応する、ブラックリスト型のソフトウェア開発では十分なリスク管理は不可能です。2
例えば、ブラックリスト型のセキュリティにより、SQLインジェクション対策は非常に大きな遠回りをしています。SQL文に於て「悪いと判っているモノ」はパラメーターに含まれる攻撃用文字列です。
- SQLインジェクション対策にプレイスホルダ/プリペアードクエリを利用する
これはパラメーターに対するインジェクション対策として正しい対策ですが、コレだけでは不十分です。
ブラックリスト型のセキュリティでよくある勘違い/悪習慣は、「悪いと判ったモノ」だけ対応して十分だとしてしまうことです。3
- SQLインジェクション対策にプレイスホルダ/プリペアードクエリだけ使っていればOK
これは明白に不十分で誤りですが、セキュリティ専門家と言われる人達まで含めて「プレイスホルダ/プリペアードクエリだけ使っていればOK」と誤った情報が広められました。
最初から正しく十分なSQLインジェクション対策が広められていれば、今頃はSQLインジェクション脆弱性を持つアプリケーションはほぼゼロになっていたと思います。残念ながらソースコード検査をすると現在でもSQLインジェクション脆弱なコードは普通に見つかり、外部から容易に攻撃できる脆弱なコードも少くありません。
ホワイトリスト派とブラックリスト派
国際情報セキュリティ標準やセキュリティガイドラインは基本的にホワイトリスト型のセキュリティ対策(リスク管理)を前提に構築されています。普通のセキュリティ専門家はホワイトリスト派です。十分なセキュリティを維持していることを保証できるようにするにはホワイトリスト型で防御せざるを得ないからです。
一方でソフトウェア開発の現場では、90年代のコンピューターリソース制限や過去のプログラミング原則/習慣から、ブラックリスト型のセキュリティ対策が一般化/常識化しました。
ブラックリスト型対策が十分なリスク制御ができないから、といって全く無用なモノにはなりません。リスク削減/緩和策としては十分に有用です。
しかし、セキュリティ専門家ならブラックリスト型対策を主たる対策、信頼可能な対策としません。基本的にブラックリスト型の対策は信頼できず、リスクを十分に制御できないからです。基本は常にホワイトリスト型対策優先です。
ブラックリスト派によくある勘違い
「ホワイトリスト型で対策しても、リスクが残って攻撃が可能。結局、リスクを全て識別し、個別に脆弱性に対応しなければならない」があります。
一見、正しいように見えますが「リスクを全て識別し、個別に脆弱性に対応する」のは非効率かつ非現実的です。
標準情報セキュリティの考え方では「ホワイトリスト型で信頼境界での全てのモノの出入りを信頼可能であることを検証/保証する。ただし、”信頼可能なモノ”にも必ず”残存リスク”がある。”残存リスク”が許容可能なレベルのリスクになるまで”多層防御”(またはリスク移転)でリスクを削減/廃除する」になります。
文章にすると複雑になっているように見えますが、考慮すべきリスクが”残存リスク”のみに限定されます。このためリスク管理は比べ物にならない程、容易になります。
ホワイトリスト型のセキュリティ対策のリスク評価がとても容易になるケースは、以下のエントリを参照すると解ります。
ホワイトリスト型のセキュリティの場合、排除しきれなかったリスク限定され、評価し易いです。例えば、HTTPヘッダーのヘッダー名と値で
- ホワイトリスト型: ヘッダー名は許可した文字列のみ。値は英数、ハイフン、アンダーバー、ドットだけを許可。最小1文字、最大128文字まで。
- ブラックリスト型:HTTPヘッダーインジェクション対策として改行文字だけ禁止。
とした場合、アプリケーション全体としてどのようなリスクが残るのか?ホワイトリスト型なら簡単です。ホワイトリスト型の場合、特定ヘッダーに危険な値を設定される(キャッシュやクッキーなど)リスクが残ります。
ブラックリスト型の残存リスク評価は困難です。改行文字を禁止しただけでは、JavaScriptインジェクション、オープンリダイレクト、危険なHTTPヘッダー設定、オーバーフロー攻撃、今年春から猛威を振るったStruts2のContent-Length:やContent-Type:を利用した任意コード実行など、ありとあらゆるリスクが考えられます。
まとめ
情報システムに限らず、セキュリティ対策とはリスク管理です。リスクを確実に管理するにはホワイトリスト型対策が必要になります。
セキュリティが重要な首相官邸では、出入りできる人をブラックリスト型(出入りしてはダメな人)で対策することはあり得ません。出入りを許可された人であっても、設備工事などの入った人が大臣と同じレベルの信頼性である、と取り扱う事もあり得ません。
IT以外の一般のセキュリティ対策と見比べても、ソフトウェアのセキュリティ対策はブラックリスト型の対策でおかしな事になっている場合があります。一度、今もっているセキュリティ常識/習慣は本当に最適化されたものか?考えてみると良いと思います。
ISO 27000が2000年から要求しているセキュアコーディングの第一原則、入力をバリデーションする、がほぼ全てのアプリケーションで十分に実施されていない原因はブラックリスト型の思考と習慣なのでは?
参考:入力バリデーションは入力データのバリデーションとビジネスロジックのバリデーションとを区別して考えます。これを混同しているケースも少くないです。
- 脆弱性としては700ちょっと、エントリとしては1000程登録されている。 ↩
- ブラックリスト型対策はリスク管理に悪影響を与えるだけでなく、性能にも悪影響を与えることがあります。例えば、壊れた文字エンコーディングを使うと正規表現ライブラリを攻撃できることがあります。壊れた文字エンコーディングは「悪いと判っているモノ」 なので文字エンコーディングバリデーションが繰り返し正規表現ライブラリ内で行われています。PCREライブラリにホワイトリスト型でバリデーションしたデータ(文字エンコーディングが正しいデータ)使いバリデーションを無効にした正規表現検索を行うと、数百倍の性能差がでることがあります。 ↩
- セキュアコーディングでも勘違いは起きています。入力処理のバリデーションは出力処理の安全性確保とは独立した安全対策ですが、独立した処理となっていないコードはよくあります。 ↩