リスト1■脆弱性のあるPerlスクリプトの例 |
$file = 'data/' . $FORM{'filename'}; if (open(FP, "$file")) { $s = join('', showMessage($s); } else { showMessage("$file がオープンできませんでした($!)"); } |
Perlは“手続き型言語”の一種であり、何らかの処理を行わせたい場合には、あらかじめ用意されている関数を呼び出すことで処理が可能となる。このことはPerlに限らず、ほかのプログラミング言語でも考え方が変わらない。何を意味するかというと、次のような具合だ。
Perlを使ったプログラムにおいて、ファイルの内容を読み込む場合、「ファイルのオープン」という処理が必要となる。ちょうど、ノートに字を書くために最初にノートを開く操作(処理)と同じである。
Perlでは「open()」という用意されている関数を使うことで、ファイルをオープン処理することができる。ここでオープンに失敗すると、関数からは「0」が返される。特殊変数「$!」を参照することで、失敗した理由を知ることができるため、デベロッパーは活用するとよいだろう。
そして重要なのは、リスト1のプログラムにはセキュリティホールが存在することだ。
「open()」の第2引数でユーザーが入力した文字列をそのままプログラムに渡している点にあるのだ。「そのまま」という点がとても脅威になってしまうのである。
CGIスクリプトは、主にUnix(Linux)などのプラットフォーム上で動作するが、Unixでは相対パス「..」指定によって上位のディレクトリへたどることができる。
例えば、フォームのテキストボックスに、「../../../../etc/passwd」という入力が含まれている場合、パスワードファイルの中身が盗み取られてしまう。この場合のセキュリティホールは、サーバ側の情報が外部へ漏えいすることを意味しており、サーバ全体がクラックされる原因につながる可能性が高い。
このため、CGIスクリプト内では相対パスを禁止する指定が必須であり、ファイル名のパスをユーザーに入力させない作り込みも必要なのだ。また、セキュリティポリシーの一つとして、重要な情報を扱うサーバでは入力フォームを絶対に利用しないというケースも多い。
Perlの「open()」には、相対パスのほかにもセキュリティホールとなってしまうケースがある。
Unixには、「パイプ」という複数のコマンドを並列に実行させながら、1つのコマンドの標準出力を後続コマンドへと引き継がせる機能が備わっている。具体的には、パイプは「|」(バーティカルバー)で指定する。以下に例を示そう。
■パイプ指定の書式 |
# [コマンド1] | [コマンド2] | [コマンド3] | …… |
Windowsのコマンドプロンプトでも「|」を使うことができる。しかし、単純に先頭から順にコマンドを実行しているだけのため、厳密にはパイプとはいえない。つまり、前のコマンドの実行が終わるまで、次のコマンドの起動が待たされるからだ。
パイプはコマンドライン操作には欠かせない仕組みではあるが、実はPerlの「open()」でもそのまま使えてしまう。例えば、$file以降にパイプを使って外部コマンドを指定すると、外部コマンドが起動できてしまう。CGIスクリプトのWebフォームからの入力データチェックに不備があると、クライアントPCのユーザーがWebサーバの情報を不正に盗み出すことも可能となってしまうのだ。
リスト■不当な指定を排除するようにしなければならない |
open(FP, "$file | cat /etc/passwd | sendmail -t -i …"); |
また、CGIスクリプトはWebサーバの権限で起動されるため、Webサーバソフトの起動にroot権限が使われていると、CGIスクリプト自身もroot権限で動作することになる。そうすると、CGIスクリプトから外部コマンドが実行できてしまった場合、事実上サーバ上の操作を何でもできてしまうことになる。この点は、ディストリビューションで対処したApacheなどのパッケージが用意されており、「httpd」や「Apache」などのユーザーで起動するよう標準設定されている。
Unixでは上記のパイプのほかに、「;」によるコマンドの連続実行、「&&」による前のコマンドが正常終了すると、次のコマンドへと実行権を移すコマンド、「||」では前のコマンドが異常終了すると、次のコマンドを実行するというものがある。これらの文字についても、CGIスクリプト側で除外するような仕掛けが必要だろう。
Copyright © ITmedia, Inc. All Rights Reserved.