次のページ 前のページ 目次へ

4. 全ての入力を検証する

入力によっては信頼できないユーザから行われることがあり,こういった場合 には入力を使用する前に検証(フィルタリング)する必要がある. 何が正しいかを決めておき,その定義に合わないものは全て排除すべきである. その逆(何が正しくないかを決めておき,それに当てはまるものを排除する)を 行ってはならない.なぜなら,重要なケースを処理し忘れるかもしれないから である. 文字列の最大の長さ(必要なら最小の長さ)を制限しておき,その長さを超えた ときにも制御を失わないようにすること(この問題についての詳しい説明は, 「バッファオーバーフロー」の節を参照すること).

文字列の場合は,正しい文字やパターンを(例えば正規表現として)決めておき, それに当てはまらないものを全て排除すること. 文字列が制御文字(特に改行文字や NIL 文字)やシェルのメタ文字を含むとき には特別な問題が起きる.したがって,このようなメタ文字は入力された時点 ですぐに「エスケープ」し,間違って送られてしまわないようにするとよい だろう. CERT はこれをさらに進めて,エスケープする必要がない文字のリストに含ま れていない全ての文字をエスケープすることを推奨している[CERT 1998, CMU 1998]. 詳しくは「呼び出しの際に正しい値だけを使う」の節を参照すること.

全ての数値について,許可する最小値(0 のことが多い)と最大値を決める. ファイル名はチェックすべきだ.普通は「..」(上位ディレクトリ)は正しい値と は認めないとよいだろう. ファイル名では,ディレクトリの変更は全て禁止するとよいだろう.例えば, 「/」を正しい文字の集合に含めないなどの方法がある. 電子メールアドレスを完全にチェックしようとすると非常に面倒である.なぜ なら,全てのアドレスに対応しようとしても,検証が非常に面倒な古い形式の アドレスが存在するからである.このようなチェックが必要であれば, mailaddr(7) と IETF RFC 822 [RFC 822] に詳しい情報がある.

これらの検査は普通,一ヶ所で集中して行うべきである. なぜなら,後で正当性の検査自体の正確さの確認を行うことが容易になるから である.

正当性の検証するコードを自分で作った場合は,それが実際に正しく動作する ことを確かめること.このことは, 別のプログラムが使う入力(ファイル名や電子メールアドレス,URL 等)を チェックする場合には特に重要である. このようなテストには気づきにくい間違いがあることが多く,いわゆる 「代理人問題」(チェック用のプログラムの前提が,実際にデータを使う プログラムの前提と異なる問題)を起こすことがある.

以下の節では,プログラムへ与える様々な種類の入力について説明する. この入力には環境変数や umask 値など,プロセスが持っている状態も含む点 に注意すること. 必ずしも全ての入力を信頼できないユーザが行うわけではないので,注意する 必要があるのは信頼できないユーザからの入力だけでよい.

4.1 コマンドライン

多くのプログラムはコマンドラインを入力のインタフェースとして用い,引数 として渡すことにより入力を受け取る. setuid/setgid されたプログラムは信頼できないユーザからコマンドライン 入力を受け取ることがあるので,自分自身で防御しなければならない. また,ユーザはコマンドラインをかなり自由に扱うことができる(execve(3) 等のシステムコールを用いる). したがって,setuid/setgid されるプログラムはコマンドライン入力を検証し なければならないし,コマンドライン引数 0 が示すプログラム名を信用して はならない(ユーザはプログラム名に NULL を含めた自由な値を設定できる).

4.2 環境変数

デフォルトでは環境変数は親プロセスから引き継がれる. しかし,あるプログラムが他のプログラムを実行する際には,環境変数を任意 の値に設定することができる. これは setuid/setgid されるプログラムにとって危険である.なぜなら,このよ うなプログラムを呼び出す元のプログラムは環境変数を制御して送り付けるこ とができるからである. 環境変数は普通は引き継がれるので,この危険性も引き継がれていく.

環境変数は,同じフィールドに複数の値を持てる形式で保存される(例えば SHELL 変数を 2 つ持てる). 普通のコマンドシェルではこのような設定は禁止されているが,クラッカーは このような状況を作ることができる.つまりプログラムは 1 つの値しかチェッ クしないが,実際には別の値が使われることもある. さらに悪いことに,多くのライブラリやプログラムは環境変数で制御されてい るが,制御方法があいまいだったり,分かりにくかったり,そもそも文書化さ れていないことさえある. 例えば,shbash は IFS 変数を使ってコマンドライン 引数を区切る文字を決める. シェルはいくつかの低レベルシステムコールを使って呼び出されるので, IFS に普通でない値を設定することにより,安全そうに見えるシステムコール を破壊することができる.

setuid/setgid されたプログラムを安全にするには,まず(もし存在するなら) 入力として必要な環境変数の短いリストを注意深く取り出す. 次に,大域変数 environ に NULL を設定することにより環境変数 全てを消去し,その後に必要な環境変数の小さい集合に安全な値を再設定する (ユーザが指定した値は含めない). このような値としては PATH(プログラムを探す対象のディレクトリのリスト. カレントディレクトリを含めてはならない), IFS(デフォルトの `` \t\n'' を設定すること), TZ(タイムゾーン)等がある.

4.3 ファイルデスクリプタ

プログラムには「オープンしたファイルデスクリプタ」の集合が渡される. これは予めオープンされているファイルである. setuid/setgid されたプログラムは,オープンするファイルをユーザが (パーミッションの制限内で)選べるという事実に対処できなければならない. setuid/setgid されたプログラムは,新しいファイルをオープンしたときに常 に決まったファイルデスクリプタ ID でオープンされると仮定してはならない. 標準入力,標準出力,標準エラー出力が端末であることや,あるいはオープン されていることすら仮定してはならない.

4.4 ファイルの内容

あるプログラムが指定されたファイルから指示を受ける場合は,信頼している ユーザしかファイルの内容を制御できない場合を除き,そのファイルを特別に 信頼してはならない. つまり,信頼できないユーザはそのファイルやディレクトリ,そしてファイル の全ての親ディレクトリを編集できてはならない. そうでない場合には,このファイルは信頼できないものとして扱わなければな らない.

4.5 CGI の入力

CGI の入力は,内部的には設定された環境変数の集合と標準入力である. これらの値は検証しなければならない.

その他の難しい点としては,CGI への入力の多くがいわゆる「URL エンコード」 ,つまり一部の値が %HH の形で書かれている形式で与えられる点がある. ここで HH はそのバイト値を表す 16 進コードである. CGI ライブラリは,入力を URL デコードして,さらにデコードによって得ら れたバイト値が適切かどうかをチェックすることにより,入力を正しく処理し なければならない. %00 (NIL) や %0A (改行) のように問題がある値を含め,全ての値を正しく扱 えなければならない. 入力を複数回デコードしてはならない.そうでないと,「%2500」のような値 の処理を誤ってしまう(まず %25 が「%」に変換され,その結果得られた「%00」 が間違って展開されて NIL 文字になってしまう).

CGI スクリプトは入力に特殊文字を含める攻撃をよく受ける. これについては上記のコメントを参照すること.

一部の HTML form は,不正な値をある程度取り除くためにクライアント側で のチェックを行う. このチェックやユーザの手助けにはなるが,セキュリティの役には立たない. なぜなら,攻撃者はこういった「不正な」値を直接ウェブサーバに送ることが できるからである. (「信頼できる経路しか信頼しない」の節で)後述するように,サーバは自分の 全ての入力についてテストを実行しなければならない.

4.6 他の入力

プログラムは必ず全ての入力を制御できていなければならない.これは setuid/setgid されたプログラムでは特に難しい.なぜなら,そのような入力 が非常にたくさんあるからである. プログラムが考慮しなければならない他の入力としては,カレントディレクトリ, シグナル,メモリマップ(mmap),System V IPC, umask (新しく生成される ファイルのデフォルトのパーミッションを決める)等がある. プログラムの起動時に,適切かつ完全に指定されたディレクトリに(chdir(2) を用いて)明示的に移動することも考慮すること.

4.7 有効な入力時間と負荷レベルの制限

タイムアウトと負荷レベルの制限は行うこと.特にネットワークからの入力 データに対してはこの制限を行うこと. さもないと,攻撃者はサービスの要求を送り続けることにより,簡単に サービス妨害攻撃を行えてしまう.


次のページ 前のページ 目次へ