PHP は、ファイルおよびディレクトリ毎に権限を設定する多くのサーバシ ステム上に組み込まれたセキュリティを提供します。これにより、ファイ ルシステム内のファイルを読み込み可能に制御することが可能になります。 全てのファイルは世界中から読み込み可能であり、このファイルシステム にアクセスした全てのユーザから読み込まれても安全であることを確認す る必要があります。
PHPは、ファイルシステムにユーザレベルのアクセスを許可するように設 計されているため、PHPスクリプトから/etc/password のようなシステム ファイルを読み込み可能としたり、イーサネット接続を修正したり、巨大 なプリンタジョブを出力したりすることができます。これから明らかにわ かることですが、読み書きするファイルを適切に設定する必要があります。
各自のホームディレクトリにあるファイルを削除する次のスクリプトを見 てみましょう。これは、ファイル管理用にWebインターフェースを使用す る場合に通常生じるような設定を仮定しています。この場合、Apacheユー ザはそのユーザのホームディレクトリにあるファイルを削除可能です。
例1 甘い変数の確認から生じるリスク
<?php
// ユーザのホームディレクトリからファイルを削除する
$username = $_POST['user_submitted_name'];
$userfile = $_POST['user_submitted_filename'];
$homedir = "/home/$username";
unlink("$homedir/$userfile");
echo "ファイルは削除されました!";
?>
username および filename はユーザフォームから投稿可能であるため、別の username および filename を投稿して 削除すべきではない削除することが可能となります。この場合、 他の何らかの形式の認証を使用するべきです。投稿された変数が、 "../etc/" と "passwd " であった場合について考えてみましょう。簡単 なコードを以下に示します。
例2 ... ファイルシステムへの攻撃
<?php
// 外部からPHPユーザがアクセス可能なハードドライブを削除します。PHPが
// ルートのアクセス権限を有している場合、
$username = $_POST['user_submitted_name']; // "../etc"
$userfile = $_POST['user_submitted_filename']; // "passwd"
$homedir = "/home/$username"; // "/home/../etc"
unlink("$homedir/$userfile"); // "/home/../etc/passwd"
echo "ファイルは削除されました!";
?>
こうした問題を防止するために必要な重要なチェック手段として以下の2 種類のものがあります。
以下に改良されたスクリプトを示します。
例3 より安全なファイル名の確認
<?php
// PHPユーザがアクセス可能なハードドライブからファイルを削除する。
$username = $_SERVER['REMOTE_USER']; // 認証機構を使用する
$userfile = basename($_POST['user_submitted_filename']);
$homedir = "/home/$username";
$filepath = "$homedir/$userfile";
if (file_exists($filepath) && unlink($filepath)) {
$logstring = "$filepath を削除しました\n";
} else {
$logstring = "$filepath の削除に失敗しました\n";
}
$fp = fopen("/home/logging/filedelete.log", "a");
fwrite($fp, $logstring);
fclose($fp);
echo htmlentities($logstring, ENT_QUOTES);
?>
しかし、これでも、傷口を塞いだことにはなりません。 ユーザが自分用のユーザログインを作成することをあなたの認証システムが 許可しており、ユーザが"../etc/"へのログインを選択した場合、システム はまたも公開されてしまいます。このため、よりカスタマイズされたチェッ クを行なう方がよいでしょう。
例4 より安全なファイル名の確認
<?php
$username = $_SERVER['REMOTE_USER']; // 認証機構を使用する
$userfile = $_POST['user_submitted_filename'];
$homedir = "/home/$username";
$filepath = "$homedir/$userfile";
if (!ctype_alnum($username) || !preg_match('/^(?:[a-z0-9_-]|\.(?!\.))+$/iD', $userfile)) {
die("Bad username/filename");
}
//etc...
?>
オペレーティングシステムにより、注意するべきファイルは大きく変化し ます。これらには、デバイスエントリ(/dev/ または COM1)、設定ファイ ル(/etc/ ファイルおよび .ini ファイル)、よく知られたファイル保存領 域 (/home/、 My Documents)等が含まれます。このため、明示的に許可す るもの以外の全てを禁止する方針とする方が通常はより簡単です。