PHPでウェブの世界と繋がろう!
menu
ホーム > PHPセキュリティー > 5 ファイルのアップロード > 5-1 アップロードファイルからの攻撃と防御策

5-1 アップロードファイルからの攻撃と防御策

Pocket

説明

ファイルのアップロードや読み込み
PHPではファイル操作のための $_FILES変数、is_uploaded_file()関数、move_uploaded_file()関数、等が用意されており、非常に簡単にアップロードファイルを処理できます。ファイルはHTMLフォームからアップロードできます。HTMLの formタグ の属性 enctype を multipart/form-data にして method を post にすることで、ファイルをアップロードすることができます。

このようにHTMLフォームからファイルをアップロードして、PHPで簡単にファイルを操作することができますが、どのようなファイルがアップロードされるかアプリケーションでは予測が出来ないので、チェックする必要があります。

ファイル名による攻撃
POINT

  • ブラウザから渡されたファイル名は汚染リスクがある
  • ファイル名が攻撃への糸口になる

ユーザーが作成するファイル名は、ユーザーが自由に指定できます。悪意のあるファイル名である可能性もあります。

例えば PHPコードが記述された .php という拡張子のファイル名をアップロードされた場合、そのファイルを実行される危険があります。また、ファイル名にパス情報を含めて、アプリケーションが意図しない情報を読み込まれたり、想定外の場所にファイルをアップロードされてしまう危険があります。その結果、アプリケーションへの攻撃へと繋がっていきます。

ファイル名をリネームする防御策
POINT

  • ファイル名を独自の名前にリネームする

ファイル名によって攻撃が行われるケースの場合、ファイル名を独自の名前にリネームすることで、攻撃を防ぐことが出来ます。サーバー上でアップロードされたファイルを操作する場合は、独自の名前にリネームするようにしましょう。

これは、ブラウザから渡されたファイル名は入力であり、汚染リスクがあるので、適切にフィルタするということです。

//フォーム等でブラウザから渡されたファイル名
$browserFileName = $_FILES['image']['name'];
//ブラウザからのファイルがアップロードされるとtempファイルが出来る
$tempFileName = $_FILES['image']['tmp_name'];

//独自のファイル名
$originalFileName = 'image_'.date('YmdHis');

//アップロードファイルがある場合
if(is_uploaded_file($tempFileName)){
	ファイルを独自の名前で保存
	move_uploaded_file($tempFileName, "/web/images/$originalFileName");
}else{
	echo '処理に失敗しました。';
	exit;
}

ファイル名にパス情報を含める攻撃
POINT

  • ファイル名はサーバー上で想定外の場所を示すことが出来る
  • ファイル名によって想定外の場所にファイルをアップロードすることが出来る

ファイル名にパス情報を含めて、ファイル名でサーバー上の想定外の場所を示すようなことが出来ます。その結果アプリケーションが意図しない情報を読み込まれる危険があります。また、ファイル名に「..」を含めることによって、httpdに書き込み権限があるディレクトリにファイルをアップロードされてしまう危険があります。
この結果、アプリケーションへの攻撃と繋がっていきます。

情報漏洩の例
以下のようなアプリケーションの仕組みがあったとします。

ユーザーごとのメッセージを

/usr/local/lib/messages

に保存しておきます。
このメッセージはユーザーがアプリケーションにログインした際に表示させます。
ログインする際はユーザーを個々に識別するのに $userid を使用します。
この $userid をログイン時にパスワードと一緒にフォームで入力させます。

USERID:($userid)
パスワード:

ログイン後、$userid はメッセージを表示させるのに使用します。
メッセージを表示させるコードを以下のようにしてインクルードするファイルを指定します。

//インクルードするファイルを指定
<?php include_once("/usr/loval/lib/messages/$userid ")?>

このような場合に $userid を

../../../etc/passwd

と入力された場合
USERID:($userid)
/etc/passwdの内容を読み込まれてしまします。

これは上記のような仕組みの場合に発生するひとつの脆弱性です。
従って、

<?php include_once("/usr/loval/lib/greetings/$userid ")?>

ここで読み込まれるファイル名をチェックする必要があります。

このようにファイルを読み込む際はファイル名の適切なフィルタが必要です。ファイル名対策として、PHPのディレクティブでリモートファイルへのアクセスを禁止するするため、php.iniでallow_url_fopenをoffにする。realpath() と basename() 関数でファイル名をチェックする。open_basedir オプションでファイルシステムへのアクセスを制限する。等を行う必要があります。

ファイル名のチェックによる防御策
POINT

  • realpath()関数とbasename()関数を組み合わせてファイル名の妥当性を検証する
  • realpath()関数は「.」「..」といった特殊文字を展開して正規の絶対パスを取得する
  • basename()関数はパスの中からファイル名の部分のみを返す

アプリケーション内でユーザーがファイル名を指定する仕組みの場合(ユーザーがアップロードしたファイル名を使用する場合など)、realpath()関数とbasename()関数を組み合わせてファイル名の妥当性を検証する必要があります。

realpath() 関数は、入力 path のシンボリックリンクをすべて展開し、 「/./」「/../」「/」などの参照をすべて解決することにより、正規化した絶対パスを返します。例えば ../../filename → /var/www/filename

basename()関数は、ファイルあるいはディレクトリへのパスを含む文字列を受け取って、 最後にある名前の部分を返します。

ファイル名を realpath()関数 で絶対パス形式に変換して、それを basename()関数 に渡してファイル名部分のみ受け取ります。

ファイル名チェックの例
以下の例では $fileName を絶対パスに展開してから、ファイル名のみ抽出します。通常、$fileName と $resultName は等しくなります。等しくない場合は悪意があるファイル名の可能性があります。

//ユーザーID
$_POST['userid'] = '/home/';
//ファイル名として使用
$fileName = $_POST['userid'];
//フィルタ
$resultName = basename(realpath($fileName ));
//検証
if($resultName !== $vetted){
	echo $_POST['userid'].'は正しいユーザー名ではありません。<br/>'."\n";
}else{
	echo $_POST['userid'].'は正しいユーザー名です。<br/>'."\n";
}
echo $resultName;

結果は

/home/は正しいユーザー名ではありません。
home

となります。

ファイルへアクセスされる攻撃と防御策
POINT

  • PHPで作成したファイルが盗み見られる
  • PHPで作成したファイルにパーミッションを設定する
  • PHPディレクティブ open_basedir を設定する

PHPで作成したファイルがユーザーや攻撃者に覗きみられる可能性があります

これを防ぐためにファイルパーミッションを設定しなければなりません。また、PHPディレクティブ open_basedir で、指定したディレクトリ以外へのアクセスを制限させます。PHPスクリプトとからアクセスできる場所を特定のディレクトリ配下に制限することができます。特定のディレクトリを指示したいときは、最後にスラッシュを付けます。open_basedir を設定することで、include や require などのファイルシステム関数をターゲットとする攻撃の可能性を減少させます。

PHPでファイルを作成する際は、umask()関数を使用して不要なパーミッションを外しておきましょう。

umask(077);//---rwxrwx を無効にする。
$fOpne = fopen("/path/failename", "W");
[PHP ruler="true" toolbar="true"]

PHPスクリプトとからアクセスできる場所を特定のディレクトリ配下に制限することができます。特定のディレクトリを指示したいときは、最後にスラッシュを付けます。

open_basedir = /path/

タグ(=記事関連ワード)

日付

投稿日:2012年7月19日
最終更新日:2012年08月29日

関連記事

このカテゴリの他のページ

この記事へのコメント

トラックバックurl

http://www.tryphp.net/phpsecurity-file/trackback/