PHP は、ファイルおよびディレクトリ毎に権限を設定する多くのサーバシ
ステム上に組み込まれたセキュリティを提供します。これにより、ファイ
ルシステム内のファイルを読み込み可能に制御することが可能になります。
全てのファイルは世界中から読み込み可能であり、このファイルシステム
にアクセスした全てのユーザから読み込まれても安全であることを確認す
る必要があります。
PHPは、ファイルシステムにユーザレベルのアクセスを許可するように設
計されているため、PHPスクリプトから/etc/password のようなシステム
ファイルを読み込み可能としたり、イーサネット接続を修正したり、巨大
なプリンタジョブを出力したりすることができます。これから明らかにわ
かることですが、読み書きするファイルを適切に設定する必要があります。
各自のホームディレクトリにあるファイルを削除する次のスクリプトを見
てみましょう。これは、ファイル管理用にWebインターフェースを使用す
る場合に通常生じるような設定を仮定しています。この場合、Apacheユー
ザはそのユーザのホームディレクトリにあるファイルを削除可能です。
例 26-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 " であった場合について考えてみましょう。簡単
なコードを以下に示します。
例 26-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
種類のものがあります。
以下に改良されたスクリプトを示します。
例 26-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, $lo gstring); fclose($fp);
echo htmlentities($logstring, ENT_QUOTES);
?>
|
|
しかし、これでも、傷口を塞いだことにはなりません。
ユーザが自分用のユーザログインを作成することをあなたの認証システムが
許可しており、ユーザが"../etc/"へのログインを選択した場合、システム
はまたも公開されてしまいます。このため、よりカスタマイズされたチェッ
クを行なう方がよいでしょう。
例 26-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)等が含まれます。このため、明示的に許可す
るもの以外の全てを禁止する方針とする方が通常はより簡単です。
PHP はファイルシステム関連の操作に C 言語の関数を使用しているので、
null バイトの処理を予期せぬかたちで行うことがあります。
C 言語では null バイトは文字列の終端を表すので、
null バイトを含む文字列があった場合に
null バイト以降の内容は文字列として処理されません。
以下に、この問題に関する脆弱性を含むコード例を示します。
例 26-5. null バイトに対して脆弱なスクリプト
<?php $file = $_GET['file']; // ここで "../../etc/passwd\0" が渡されたとします if (file_exists('/home/wwwrun/'.$file.'.php')) { // file_exists は true を返します。これは、ファイル /home/wwwrun/../../etc/passwd が存在するからです include '/home/wwwrun/'.$file.'.php'; // ファイル /etc/passwd がインクルードされてしまいます } ?>
|
|
したがって、ファイルシステム操作で使用する「汚染された」文字列は、
つねに適切に検証しなければなりません。
先ほどの例を改良したものを示します。
例 26-6. 入力を適切に検証する例
<?php $file = $_GET['file'];
// 値として与えられる可能性のある、有効な値の一覧を作成します switch ($file) { case 'main': case 'foo': case 'bar': include '/home/wwwrun/include/'.$file.'.php'; break; default: include '/home/wwwrun/include/main.php'; } ?>
|
|