第 26章ファイルシステムのセキュリティ

PHP は、ファイルおよびディレクトリ毎に権限を設定する多くのサーバシ ステム上に組み込まれたセキュリティを提供します。これにより、ファイ ルシステム内のファイルを読み込み可能に制御することが可能になります。 全てのファイルは世界中から読み込み可能であり、このファイルシステム にアクセスした全てのユーザから読み込まれても安全であることを確認す る必要があります。

PHPは、ファイルシステムにユーザレベルのアクセスを許可するように設 計されているため、PHPスクリプトから/etc/password のようなシステム ファイルを読み込み可能としたり、イーサネット接続を修正したり、巨大 なプリンタジョブを出力したりすることができます。これから明らかにわ かることですが、読み書きするファイルを適切に設定する必要があります。

各自のホームディレクトリにあるファイルを削除する次のスクリプトを見 てみましょう。これは、ファイル管理用にWebインターフェースを使用す る場合に通常生じるような設定を仮定しています。この場合、Apacheユー ザはそのユーザのホームディレクトリにあるファイルを削除可能です。

例 26-1. 甘い変数の確認から生じるリスク

<?php
// ユーザのホームディレクトリからファイルを削除する
$username = $_POST [ 'user_submitted_name' ];
$homedir = "/home/$username" ;
$file_to_delete = "$userfile" ;
unlink ( "$homedir/$userfile" );
echo
"$file_to_delete は削除されました!" ;
?>
usernameはユーザフォームから投稿可能であるため、usernameを投稿し、 他の誰かが所有するファイルを指定、削除することが可能です。この場合、 他の何らかの形式の認証を使用するべきです。投稿された変数が、 "../etc/" と "passwd " であった場合について考えてみましょう。簡単 なコードを以下に示します。

例 26-2. ... ファイルシステムへの攻撃

<?php
// 外部からPHPユーザがアクセス可能なハードドライブを削除します。PHPが
// ルートのアクセス権限を有している場合、
$username = "../etc/" ;
$homedir = "/home/../etc/" ;
$file_to_delete = "passwd" ;
unlink ( "/home/../etc/passwd" );
echo
"/home/../etc/passwd は削除されました!" ;
?>
こうした問題を防止するために必要な重要なチェック手段として以下の2 種類のものがあります。

  • PHP Webユーザバイナリに制限された権限のみを許可する。

  • 投稿された全ての変数を確認する。

以下に改良されたスクリプトを示します。

例 26-3. より安全なファイル名の確認

<?php
// PHPユーザがアクセス可能なハードドライブからファイルを削除する。
$username = $_SERVER [ 'REMOTE_USER' ]; // 認証機構を使用する

$homedir = "/home/$username" ;

$file_to_delete = basename ( "$userfile" ); // パスを取り除く
unlink ( $homedir / $file_to_delete );

$fp = fopen ( "/home/logging/filedelete.log" , "+a" ); // 削除の記録
$logstring = "$username $homedir $file_to_delete" ;
fputs ( $fp , $logstring );
fclose ( $fp );

echo
"$file_to_delete は削除されました!" ;
?>
しかし、これでも、傷口を塞いだことにはなりません。 ユーザが自分用のユーザログインを作成することをあなたの認証システムが 許可しており、ユーザが"../etc/"へのログインを選択した場合、システム はまたも公開されてしまいます。このため、よりカスタマイズされたチェッ クを行なう方がよいでしょう。

例 26-4. より安全なファイル名の確認

<?php
$username
= $_SERVER [ 'REMOTE_USER' ];   // 認証機構を使用する
$homedir = "/home/$username" ;

if (!
ereg ( '^[^./][^/]*$' , $userfile ))
    die(
'bad filename' ); // 処理せず、終了。

if (! ereg ( '^[^./][^/]*$' , $username ))
     die(
'bad username' ); // 処理せず、終了。
//etc...
?>

オペレーティングシステムにより、注意するべきファイルは大きく変化し ます。これらには、デバイスエントリ(/dev/ または COM1)、設定ファイ ル(/etc/ ファイルおよび .ini ファイル)、よく知られたファイル保存領 域 (/home/、 My Documents)等が含まれます。このため、明示的に許可す るもの以外の全てを禁止する方針とする方が通常はより簡単です。