Chapter 4. 入力されるものすべてを検証すること

 

あなたは悪い道から救い出され、暴言をはく者を免れることができる。

 旧約聖書 箴言 2 章 12 節
Table of Contents
4.1. コマンドライン
4.2. 環境変数
4.2.1. 環境変数の中には危ないものもある
4.2.2. 環境変数の保存方法にまつわる危険性
4.2.3. 解決方法――選別して、消し去る
4.3. ファイル・ディスクリプタ
4.4. ファイルの内容
4.5. Web ベースのアプリケーションの入力(特に CGI スクリプト)
4.6. その他の入力
4.7. 自然言語(ロカール)の選択
4.7.1. ロカールを選択するには
4.7.2. ロカールを動かすメカニズム
4.7.3. 正しい値
4.7.4. 結論
4.8. 文字のエンコード
4.8.1. 文字のエンコードとは
4.8.2. UTF-8 とは
4.8.3. UTF-8 のセキュリティ上の課題
4.8.4. UTF-8 の正しい値
4.8.5. UTF-8 関連について
4.9. サイトにまたがった悪意あるコンテンツ(Cross-site Malicious Content)を 防ぐ
4.10. 再表示する可能性のある HTML や URI にはフィルタをかける
4.10.1. HTML データを削除したり禁じたりする
4.10.2. HTML データをエンコードする
4.10.3. HTML データを検証する
4.10.4. ハイパーテキストリンク(URI や URL)を検証する
4.10.5. その他の HTML タグ
4.10.6. 関連事項
4.11. クエリ以外の実行に HTTP の GET 命令を使わせない
4.12. SPAM に対抗する
4.13. 入力時間と負荷レベルに制限をかける

入力には、信頼できないユーザからのものもあります。そこで、使用する前にそれら を検証(選別)する必要があります。 まず何が正しいかを定義して、その定義にマッチしないものすべてを拒否するように しなければいけません。 その逆の定義の仕方をしてはいけません(何が不正かを定義し、それらを拒否する)。 なぜなら、重大なケースをうっかり定義し忘れてしまうかもしれないからです。

ですが、検証コードが完全なのかを確認するために、テスト用(たいていは頭の中 で実行)で「不正な」値を定義するのは良いことです。 私は入力フィルタを設定した時には、頭の中でフィルタを攻撃してみて、不正な値 がフィルタを通り抜けないかを見てみます。 入力内容にもよりますが、ここでは代表的ないくつかの「不正な値」の例をあげて みます。これらは入力時にフィルタで防御する必要がある値です。 空文字や「.」、「..」、「../」、「/」や「.」ではじまる文字列や「/」もしくは 「&」を含む文字列、すべての制御文字(特に NIL や改行)もしくは「ハイビット」 の文字(特に十進数で 254 と 255)がそれです。 繰り返しますが、コードは「悪い」値でチェックすべきではありません。頭の中で あなたが書いたパタンが容赦なく入力を制限して、正しい値だけを通すのか どうかを確かめるためです。 もしそのパタンで十分に制限していなければ、注意深くパタンを再調査して、 他の問題がないか確認する必要があります。

最大文字数(適切なら最小文字数も)を制限して、文字数を超えても制御不能に ならないようにしてください (バッファオーバーフローについての詳細は Chapter 5 を見てください)。

ここではデータタイプとしてよく使われるものをいくつか挙げます。信頼できない ユーザからのデータを利用する前に、必ず検証するようにしてください。

問題を解決できないなら、 正しい文字パタンには、プログラム内部や最終的な出力に対して特別な意味を 持つ文字もしくは文字列を含めてはいけません。

これらのテストは 1 ヶ所で集中して行ってください。そうすれば、そのテストが 正しいかどうか、後になって簡単に調査できます。

正しい入力をチェックするテストが、予定した通り確実に動作するようにしてくだ さい。別のプログラムが使う入力(ファイル名や電子メールアドレス、URL 等)を チェックする場合には特に重要です。 これらのプログラムは、見落としがちな間違いを抱えていることが多く、いわゆる 「代理人問題」(データを実際に使用するプログラムとチェックするプログラム間で 前提条件が異なっているケース)が発生します。 適切な規準があるなら、それを見てください。あわせて、そのプログラムが、拡張 機能を持っていないかどうかの調査もしてください。拡張機能は知っておく必要が あります。

ユーザの入力を解析している間は、一時的に特権すべてを落とすというのは良い 考えです。また独立したプロセスを作成するのも、同じく良い考えです (解析を行う場合は常に特権を落とし、他のプロセスが解析の要求に対してセキュ リティ上のチェックを行う)。 このケースがとりわけ当てはまるのは、解析作業が複雑である場合(たとえば、lex や yacc といったツールを使う)や、プログラミング言語がバッファオーバーフローを 防げない場合です(たとえば、C や C++)。 特権を最小限にする方法については Section 6.4 を 見てください。

セキュリティ上の判断を行う際にデータを使用する時には(たとえば「このユーザを 通過させなさい」)、必ず信頼できる経路を使ってください。 たとえば、公開されたインターネット上では、マシンの IP アドレスやポート 番号だけにユーザの認証を任せてはいけません。というのは、この情報を(もしか すると悪意を持った)ユーザが設定できてしまう環境が大多数だからです。 詳しい情報は Section 6.11 を見てください。

下記のサブセクションでは、プログラムに対するさまざまな入力について論じます。 環境変数や umask 値等、プロセスの状態を含む入力には注意が必要です。 入力すべてが信頼できないユーザによってコントロールされているわけではないので、 これから論じる入力だけを気にかければ OK です。