入力には、信頼できないユーザからのものもあります。そこで、使用する前にそれら を検査(選別)する必要があります。 まず何が正しいかを定義して、その定義にマッチしないものすべてを拒否するように しなければいけません。 その逆の定義のしかたをしてはいけません(何が不正かを定義し、それらを拒否する)。 なぜなら、重要なケースの定義をうっかり忘れてしまうかもしれないから です。 文字列長の最大値を制限してください(必要があるなら最小値も)。そして、長さを 超えてしまった場合でもシステムが暴走しないことを確かめてください (下記のバッファオーバーフローのセクションでもう少し詳しく述べますので、 見てください)。
文字列の場合は、そのシステムにとって正しいキャラクタと正しいパターン(たと えば正規表現など) を明らかにしておき、その形式に合わないものすべてを拒否 するようにしてください。 文字列にコントロールキャラクタ(特に改行や NIL)やシェルのメタキャラクタが 含まれている場合、普通の文字列では起こり得ない問題が生じます。問題を避ける ために、そのようなメタキャラクタが入力されたらすぐに「エスケープ」して、 間違ってプログラムに送られることがないようにするのが一番です。 CERT はこの考え方をさらに推し進めて、エスケープする必要がないキャラクタの 一覧に載っていないものすべてをエスケープすることを推奨しています [CERT 1998, CMU 1998]。 詳細については、下記の「正しい値でだけ呼び出すこと」を参照してください。
数字すべてに対して、許容できる最小値(たいていはゼロ)と最大値を設けましょう。 ファイル名はチェックしなければいけません。一般的に「..」(上位ディレクトリ)を 正しい値と見なしてはいけません。 ファイル名を表わす場合には、ディレクトリの変更となる動作をどんな場合でも禁止 することが一番です。たとえば、「/」を正しいキャラクタの仲間に入れてはいけま せん。 電子メールのアドレスを完全にチェックすることは、現実的にとても困難です。 というのも、すべてのケースを真面目にサポートしようとすると、アドレスの中には 正しい形式ではあるものの、非常に複雑な検証を必要とするものが存在するからです。 もしそのようなチェックが必要なら、詳細は mailaddr(7)と IETF RFC 822 [RFC 822] を見てください。
訳註: IETFは、Internet Engineering Task Force の略称で、インター ネットに関連する技術の標準化を進めるために設立された団体です。ここが発行 する文書が RFC(Requests For Comment)です。
これらのテストは 1 箇所で集中して行なうようにしてください。そうすれば後で このテストに間違いがないかの調査を簡単に済ませられます。
正しい入力をチェックするテストが、本当に予定した通りに動作するかを確認して ください。 別のプログラムが使う入力(ファイル名や電子メールアドレス、URL 等)をチェック する場合には特に重要です。 これらのプログラムは、見落としがちな間違いを抱えていることが多く、 いわゆる「代理人問題」(データを実際に使用するプログラムとチェックするプロ グラムの前提条件が異なっているケース)です。
下記のサブセクションでは、プログラムに対する様々な入力について論じます。 この入力には環境変数や umask 値など、プロセスが持っている状態も含む点に 注意してください。 必ずしもすべての入力が信頼できないユーザによって行なわれているわけでは ありません。注意する必要があるのは信頼できないユーザからの入力だけです。
プログラムの中には、入力のインターフェースとして、コマンドラインを使用する ものが多数あります。この場合、引数を渡すことによって入力とします。 setuid/setgid されたプログラムは、信頼できないユーザからコマンドラインに よる入力を受け取る場合があるので、そのプログラム自身で対処する必要があります。 一般的にユーザは、コマンドラインを自由に扱えます(execve(3)のようなシステム コールを使って)。 したがって、setuid/setgid されたプログラムは、コマンドラインからの入力を検査 する必要があり、コマンドラインの引数 0 番に当たるプログラム名を信用しては いけません(ユーザは NULL を含むどんな値も設定できるからです)。
環境変数は、デフォルトでは親プロセスから継承されます。 しかしあるプログラムから他のプログラムを実行(exec)した場合、環境変数に 任意の値を設定できます。 setuid/setgid されたプログラムでは、これは危険をともないます。というのも プログラムを呼び出すことで環境変数のコントロールが可能になり、環境変数を 他のプログラムに渡せてしまうからです。 普通、環境変数は継承されてしまうため、この危険性も同時に引き継がれてしまい ます。
環境変数は、同じフィールドに複数の値を設定できる形式で記憶されています (たとえば SHELL 変数には、2 つの値を設定できる)。 コマンドシェルの代表的なものは、この設定ができないようになっていますが、 クラッカーは、そのような状況を作り上げられます。つまりこのケースならば、 プログラムで 1 つの値はチェックしますが、実際は別の値を使用してしまう ことが考えられます。 さらに悪いことに、ライブラリやプログラムはたいていの場合環境変数によって 制御されているものの、その方法があいまいだったり、わかりにくかったり、 中にはドキュメント化されていないものがあったりします。 たとえば、IFS 変数は sh や bash でコマンドラインの引数を 分割するのに使用されるキャラクタを指定するために利用されています。 シェルは低レベルのシステムコールを利用して呼び出されるため、IFS 変数に 異常な値を設定すると、一見安全と思われるシステムコールを危険なものに 変えてしまう恐れがあります。
setuid/setgid されたプログラムを安全にするには、環境変数の中から入力(もし あれば)に必要とされるものを注意を払って選び出し、短いリストを作る必要が あります。 そして環境変数全体を表す大域変数である environ に NULL を設定して、 環境変数全体を削除し、その後に必要となる最小限の安全な値を再設定してください (ユーザの設定値は使用「しない」こと)。 環境変数には、PATH(プログラムのありかを検索するディレクトリのリストです。 これにカレント・ディレクトリを入れては「いけません」)、IFS(デフォルトでは 「\t\n」です)、TZ(タイムゾーン)があります。
プログラムには「オープンしたファイル・ディスクリプタ」、つまりあらかじめ オープンされているファイルが渡ります。 setuid/setgid されたプログラムでは、ユーザがあるファイルをオープンして、 それを利用できてしまう(パーミッションの制限内で)ということを気にする必要が あります。 setuid/setgid されたプログラムでは、新しくオープンしたファイルが常に固定した ファイル・ディスクリプタ ID に割り当てられていると想定してはいけません。 また端末が標準入力、標準出力、標準エラー先になっていること、また端末が すでにオープンされていることも前提にしてはいけません。
あるファイルの内容によって、プログラムの動作が左右される場合、信頼できる ユーザだけがその内容を変更できるのでなければ、そのファイルを信用してはいけ ません。 つまり、信頼できないユーザが、ファイルやそのファイルがあるディレクトリ、 その親ディレクトリを修正できてはいけません。 そうでなければ、そのファイルを信頼するに値しないものとして扱わなければ なりません。
CGI からの入力は、実際のところ環境変数や標準入力として扱われます。 したがってこれらも検証しなければなりません。
CGI からの入力の多くが、いわゆる「URL エンコードされた」形式になっている 点が検証をより厄介にしています。つまり 16 進数の HH というバイト値を表す には %HH という形式をとります。 CGI や CGI ライブラリは、これらの入力を適切にデコードして、バイト値が正しい かどうかをチェックする必要があります。 %00 (NIL) や %0A (改行)のような疑わしい値を含むすべての入力を間違いなく処理 しなければいけません。 入力のデコードは 1 回だけにしてください。でないと、「%2500」のような入力が 誤って処理されてしまいます(まず %25 が「%」に変換され、その結果「%00」が 間違って NIL キャラクタに変換されてしまいます)。
入力に特殊なキャラクタを混ぜることで、CGI スクリプトを攻撃するケースがまま 見られます。上記の解説を見てください。
HTML のフォームには、クライアント側でチェックをすることで不正な値を排除する ものもあります。 これはユーザにとっては有益かもしれませんが、セキュリティ上は無意味です。 というのも、攻撃者はそのような「不正」な値を直接 Web サーバーに送り付けられる からです。 後で(「信頼できる経路だけ信じること」のセクション)説明しますが、サーバーは 自分が受け取るすべての入力をチェックする必要があります。
プログラムは、入力のすべてをコントロールすることが必須です。 しかし setuid/setgid されたプログラムでは困難を極めます。理由は、そのような 入力があまりに多いからです。 一方、入力プログラムでは下記の点を考慮する必要があります。
タイムアウトと負荷レベルの制限を設けてください。特にネットワーク経由で やってくるデータには必ず制限をかけてください。そうしないと攻撃者は絶えること なくサービス要求を送り付けることで、いとも簡単にサービス妨害攻撃を実行 できます。