PHPによるファイルアップロード処理のあれこれ

2015年6月11日(木)
原田 裕介(はらだゆうすけ)

みなさん、こんにちは。第6回となる今回は、PHPでの画像ファイルのアップロードについてお話しします。

参考図書である「プログラミングPHP」には、画像のアップロードについて、実は1ページ半程度しか載っていません。

ですが業務でWEBシステムを開発していると、画像のアップロードは頻繁に利用するうえに、プログラムとして割と手間がかかります。さらにはセキュリティホールにもなりやすい厄介な部分ですので、しっかり見ていきましょう。

まずはアップロードしてみよう

なにはともあれ、まずは普通にファイルをアップロードするプログラムを書いてみます。

normal_post1.php source

<?php
if($_FILES['file']){
  move_uploaded_file($_FILES['file']['tmp_name'], './img/test.jpg');
}
?>

<form action="./normal_post1.php" method="POST" enctype="multipart/form-data">
  <input type="file" name="file">
  <input type="submit" value="ファイルをアップロードする">
</form>

ブラウザでアクセスして画像を投稿すると、imgディレクトリの下に投稿した画像が格納されていることがわかります。アップロードの流れは、以下の図のようになります。

GETメソッドは、画像ファイルのようにサイズの大きいものを送るには容量が小さすぎて足りないので、必ずPOSTメソッドを指定します。

enctype属性には、「MIMEタイプ」と呼ばれるフォームで送るデータの形式を指定します。通常のフォームではあまり設定されることはありませんが、画像アップロードの際は必須です。

「<input type="file" name="file">」は、フォームの中で実際にユーザーがアップロードする画像を指定するパーツです。

$_FILES['file']にはどんな情報が入っているのでしょうか? 次のプログラムで見てみましょう。

normal_post2.php source

<?php
if($_FILES['file']){
  print_r($_FILES['file']);
}
?>

<form action="./normal_post2.php" method="POST" enctype="multipart/form-data">
  <input type="file" name="file">
  <input type="submit" value="ファイルをアップロードする">
</form>

正常に画像をアップロードした場合

normal_post2.php result

Array ( [name] => 907b3b48.jpg [type] => image/jpeg [tmp_name] => D:\xampp\tmp\php1C39.tmp [error] => 0 [size] => 11957 )

画像がアップロードできてない場合

normal_post2.php result

Array ( [name] => [type] => [tmp_name] => [error] => 4 [size] => 0 )

上述した2つの例のように、配列の中には以下の5つの項目がありました。

項目内容
nameアップロード時のファイル名
type画像のMIMETYPE
tmp_nameサーバで保存した画像のフルパス
errorエラーコード。画像が正常にアップロードできると0、失敗するとエラーコード
size画像のサイズ。単位はバイト

エラーチェック

単純に画像をアップロードするだけであれば、上記のプログラムで十分ですが、実際には当然エラー処理なども必要になります。ここでは、画像がアップロードされているかどうかや、例外処理などを追加してみます。

error_check1.php source

<?php
try{
        if(is_uploaded_file($_FILES['file']['tmp_name'])){
                move_uploaded_file($_FILES['file']['tmp_name'], './img/test.jpg');
        }
}catch(Exception $e) {
        echo 'エラー:', $e->getMessage().PHP_EOL;
}
?>

<form action="./error_check1.php" method="POST" enctype="multipart/form-data">
        <input type="file" name="file">
        <input type="submit" value="ファイルをアップロードする">
</form>

is_uploaded_fileで実際にファイルがアップロードされたかを調べることに加え、try-catch構文で例外処理をしています。

画像アップロード関連の設定について

画像アップロードに際しては、色々な設定値があります。代表的なものだと、アップロード可能なファイルの最大サイズがありますね。実際のプログラムでは、必ずといって良いほど設定します。

また値の設定方法もいくつかあり、項目によってどこで設定できるかはだいぶ異なるので注意しましょう。どこで設定できるかなど、試験としては非常に問題にしやすいところですね。

ファイルアップロードに関連する設定ができる箇所は、主に以下の5箇所です。

  • htmlフォーム
  • php.ini
  • ini_set関数
  • httpd.conf(Apacheを使用している場合)
  • htaccess

では、それぞれ見ていきましょう。

htmlフォーム

htmlでフォームを作成する際に、以下のようにinputタグでMAX_FILE_SIZEを設定すると、アップロードされるファイルの最大サイズをバイト単位で指定できます。

<input type="hidden" name="MAX_FILE_SIZE" value="4194304" />

ただしこの項目は、ブラウザに対してアップロードのサイズ制限をかけるだけなので、ブラウザ側でごまかしてしまうことができます。そのため、セキュリティ目的で設定するのではなく、サーバ側の容量制限を超えるファイルは長時間かけて送信を試みた結果、弾かれるといった形で、ユーザーに手間をかけたり、サーバのリソースの無駄使いを避けたりするために設定します。

php.ini

言わずと知れたPHPの主な設定を行うファイルです。ここで設定できる項目はたくさんありますが、ファイルアップロードに関係するものとしては、「post_max_size」と「upload_max_filesize」、「memory_limit」、「max_execution_time」などがあります。

file_uploadsこの項目をオンにしてファイルアップロードを有効にします。
post_max_sizePOSTメソッドで送信されるデータの容量制限値です。
upload_max_filesizeファイルをアップロードする際の容量制限値です。post_max_size以下の数値を指定します。ファイルサイズの制限はhtmlフォームではなく、ここをメインとして制限をかけます。
memory_limitPHP実行時のメモリ制限です。大きなファイルをアップロードするようなスクリプトの場合、その分メモリも大量に消費するため、大きい値を設定する必要があります。
max_execution_timePHPの最大実行時間です。大きなファイルをアップロードする場合その分実行時間も伸びるので、ここも合わせて設定しますが。むやみに大きい値を設定しすぎるとメモリリークが起きた際などにサーバがメモリ不足でハングアップする原因となります。
upload_tmp_dirファイルがアップロードされる一時ディレクトリ。
max_file_uploadsアップロードできるファイルの最大個数。

ini_set関数

ini_setはphp.iniの設定値をphpで実行している最中に一時的に変更する関数です。全ての値を変更できるわけではないですが、アップロードに関係ある「post_max_size」と「upload_max_filesize」、「memory_limit」、「max_execution_time」は対応しています。ただし、セーフモードの場合は「max_execution_time」などは変更できないので注意しましょう。

httpd.conf

WEBサーバにApacheを使用している場合になりますが、Apacheの設定項目です。

LimitRequestBodyクライアントからサーバに対して送られてくるデータの制限値。php.iniのpost_max_sizeより少し大きな値に設定します。

htaccess

htaccessは、httpd.confやphp.iniで設定している項目の一部を変更できます。ファイルを置くだけなので手軽に使えますが、色々なところにファイルを置いて複雑化してしまうことが良くあるので、必要がない限りはhttpd.confやphp.iniで設定するようにした方が良いでしょう。

設定に関するまとめ

php.iniで設定できる項目は、「順当にphp.iniに設定する方法」と「htacccessから設定する方法」、「ini_setで実行中のPHPスクリプトから設定する」の3パターンがあります。

どこから設定しても設定自体はできますが、htaccessは上記の通りオススメしていません。またphp.iniでの設定は、全てのスクリプトに適用されてしまうので、「post_max_size」と「upload_max_filesize」はセキュリティ的に問題ですし、「memory_limit」や「max_execution_time」はメモリリークやスクリプトが暴走した際のパフォーマンスに悪影響があります。そのためこれらの項目は、必要に応じてini_setで設定すると良いのではないでしょうか。

セキュリティについて

フォームにはユーザーから任意の値が送られてくるので、PHPプログラムではセキュリティホールとなることが多いものですが、もちろんファイルのアップロードも例外ではありません。それどころか、処理によっては悪意のあるファイルをそのままアップロードできたりもしてしまうので、より安全に配慮して開発を行う必要があります。

ファイル名の決め方

送られてきたファイルを、送られてきた時のファイル名をそのまま使ってサーバに保存するようなソースコードを良く見かけますが、このような書き方は危険です。

up_security1.php source

<?php
try{
        if(is_uploaded_file($_FILES['file']['tmp_name'])){
                move_uploaded_file($_FILES['file']['tmp_name'], './img/'.$_FILES['file']['name']);
        }
}catch(Exception $e) {
        echo 'エラー:', $e->getMessage().PHP_EOL;
}
?>

<form action="./error_check1.php" method="POST" enctype="multipart/form-data">
        <input type="file" name="file">
        <input type="submit" value="ファイルをアップロードする">
</form>

ファイル名は、ユーザーからの入力値を使わず、必ずプログラム側で指定しましょう。セキュリティ的にも意味がありますし、すでにアップロードされたファイルとファイル名が被らないようにするためでもあります。

アップロードできるファイルの拡張子を制限する

通常、ファイルアップロード機能は、どのような形式のファイルもアップロードできてしまいます。PHPのファイルをアップロードさせてサーバ上で実行されてしまったり、拡張子がexeの実行ファイルをアップロードさせて他のユーザーにダウンロードさせてしまったりすれば、重大なセキュリティ事故につながることになりえます。

たいていの場合アップロードしたいファイルは、画像などの特定の種類に限定されますので、jpg、png、gif(画像の場合)などに拡張子を限定して、それ以外はアップロードを拒否するホワイトリスト形式でチェックするのが良いでしょう。

ファイルのアップロードに関しては非常に奥が深く、説明しきれていない部分もあります。セキュリティに関しては、常に新しい脆弱性が出てくるものですので、良く確認して実装していくことをオススメします。

次回は「httpキャッシュについて」をお話ししていきます。
ではまた。

著者
原田 裕介(はらだゆうすけ)
株式会社ユーザーローカル
東京都在住。携帯のホームページ作成サービスでHTMLのコーディングを始めて、PHPerとなる。
株式会社ハッシュシステム代表取締役やtetolの立ち上げ、株式会社イードでニュースメディアの開発やエンジニア採用を経て、現在はアクセス解析ツールの開発を行いながら、個人でもWEBサービスの開発を行っている。PHP技術者認定上級試験の認定者でもあり、受賞歴はmixi scrap challenge優勝など。

 

連載バックナンバー

Think ITメルマガ会員登録受付中

Think ITでは、技術情報が詰まったメールマガジン「Think IT Weekly」の配信サービスを提供しています。メルマガ会員登録を済ませれば、メルマガだけでなく、さまざまな限定特典を入手できるようになります。

Think ITメルマガ会員のサービス内容を見る

他にもこの記事が読まれています