お世話になります。PHPでファイルをアップロードする仕組みを考えており、ここ3日ほど、色々なサイトで紹介されているスクリプトを拝見しました。
(ここteratailの他のスレッドでも書かれていましたが)注意すべき点として挙げるべき点は、
- 日本語がファイル名に含まれていないか?
- ファイル名にA-Za-z_-. 以外の文字が含まれていないか?
- POST時にエラーが発生していないか?
- ファイル名が空でないか?
- 不正なパスを表す文字列がファイル名に含まれていないか?
- ファイルサイズが0バイトより大きく指定値以内か?
- 本当にアップロードされたファイルなのか?
- 既存ファイルが存在していないか?
- ファイル名をハッシュ化して移動・保存。
- png,jpg,pdfなど拡張子を制限する。
- SWFファイルの制限。
- 保存フォルダを公開ディレクトリから外す
であるとまとめたわけですが、紹介されているサンプルコードは、簡素化したものか、もしくは私には難易度の高いもの両極端だったので、上記点を(12以外はカバーしてる?)踏まえたコードを作ってみました。
しかし、これで良いのか自信がなく、また身近に見てくれる人がいないため、ご指摘いただければ幸いと考え投稿しました。
何処かおかしなところがありましたらご指摘いただけないでしょうか?
ちなみに、アップを受付ける拡張子は、png,jpg,pdfの3種です。
よろしくお願いいたします。
<?php $maxsize = $_POST["MAX_FILE_SIZE"]; $extension = array("image/jpeg", "image/png", "application/pdf"); $ds = DIRECTORY_SEPARATOR; $storeFolder = "temp_dir"; //同じディレクトリ内にある一時保存用ディレクトリ $DirPath = dirname( __FILE__ ).$ds.$storeFolder.$ds; $CookieData = $_COOKIE["CookieData"]; //事前にユーザ毎に付与しているランダムな文字列 //[アップロード]ボタンの押下確認 if (isset($_POST["upload"])) { foreach($_FILES["upfile"]["tmp_name"] as $no => $tmp_name){ //エラーはないか? if(!empty($_FILES["upfile"]["name"][$no]) && empty( $_FILES["upfile"]["error"][$no])){ //サイズは超えていないか? if(($_FILES["upfile"]["size"][$no] > 0) && ($_FILES["upfile"]["size"][$no] < $maxsize)){ //アップロードされたファイルか? if(is_uploaded_file($_FILES["upfile"]["tmp_name"][$no])){ //アップロードされたファイルを確認 //拡張子を判定する $type = mime_content_type($_FILES["upfile"]["tmp_name"][$no]); if(in_array($type, $extension)){ //一時保管されているファイル名 $tempFile = $_FILES["upfile"]["tmp_name"][$no]; //アップロードされたファイル名 $filename = $_FILES["upfile"]["name"][$no]; $file = explode(".", $filename); $ext = array_pop($file); //md5でハッシュ化。別のユーザーと同名のファイル名であったとしても被らないよう、 //事前にユーザ別にランダムな文字列をcookieに持たせており、その値をファイル名へ。 //また、ファイル数に応じて、番号も付与。 //ファイル名を取得する際、basename()を用いることでファイルシステムトラバーサル攻撃は防げる //とのことだが、ハッシュ化すればその対策は必要ないのだろうか?? $new_filename = $CookieData."-".$no.md5($filename).".".$ext; //設定するファイル $targetFile = $DirPath.$new_filename; //一時保管用にハッシュ化したファイルをセット if(move_uploaded_file($tempFile,$targetFile)){ //*** 画像ファイルだったらサイズ変更 *** if($type != "application/pdf"){ $upfile = $storeFolder."/".$new_filename; list($width, $height, $mime_type) = getimagesize($upfile); if($width > 1000){ //1000pxを超えるとき $hiritsu = 1000 / $width; $width_edit = floor($width * $hiritsu); $height_edit = floor($height * $hiritsu); switch($mime_type){ //jpegの場合 case IMAGETYPE_JPEG: $src = imagecreatefromjpeg($upfile); break; //pngの場合 case IMAGETYPE_PNG: //拡張子の設定 $src = imagecreatefrompng($upfile); break; } $dst = imagecreatetruecolor($width_edit, $height_edit); imagecopyresampled($dst, $src, 0, 0, 0, 0, $width_edit, $height_edit, $width, $height); imagejpeg($dst, $upfile); } }//********************** $error .= $no."成功です。<br />"; } else{ $error .= $no."失敗です。<br />"; } } else{ $error .= $no."この拡張子は利用できません。<br />"; } } else{ $error .= $no."ファイルがアップロードされていません。<br />"; } } else{ $error .= $no."ファイルサイズが指定サイズを超えています。<br />"; } } else{ $error .= $no."ファイルアップロードエラーです。<br />"; } } } ?> <html lang="ja"> <head> <meta charset="utf-8"> <script> <!-- //アップロードを許可する拡張子 var allow_exts = new Array('jpg', 'jpeg', 'png', 'pdf'); //フォーム内容の確認をする関数 function checkForm() { var files = document.getElementsByName('upfile[]')[0].files; //ファイルが選択されているか確認 if (files.length == 0) { alert('ファイルを選択してください'); return false; } for (var i = 0; i < files.length; i++) { if (!checkExt(files[i].name)) { alert(files[i].name+'はアップロードできません'); return false; } } return true; } function checkExt(filename) { var ext = getExt(filename).toLowerCase(); if (allow_exts.indexOf(ext) === -1) return false; return true; } function getExt(filename) { var pos = filename.lastIndexOf('.'); if (pos === -1) return ''; return filename.slice(pos + 1); } --> </script> </head> <body> <h1>アップロード処理</h1> <?php echo $error; ?> <form action="./" enctype="multipart/form-data" method="post" onsubmit="return checkForm();"> <input type="hidden" name="MAX_FILE_SIZE" value="5097152"> <p><input type="file" name="upfile[]" accept=".jpg,.png,image/jpeg,image/png,application/pdf" /></p> <p><input type="file" name="upfile[]" accept=".jpg,.png,image/jpeg,image/png,application/pdf" /></p> <p><input type="file" name="upfile[]" accept=".jpg,.png,image/jpeg,image/png,application/pdf" /></p> <p><input type="submit" name="upload" value="アップロード"></p> </form> </body> </html>
■補足説明
皆様からのご指摘、真摯に受け止めたく思います。
今回の質問に至った経緯を改めてお伝えすると、今回、ファイルアップロードを調べているうちに、色々な危険性があることを知りました。
はじめの方は、何か良いサンプルコードはないものか?そんな気持ちで居ましたが、当然とは思いながらも初心者向けのものが多いのが実情。その反面、高度な内容のものもありましたが、私には難易度が高かったため、自分で理解をしながらコードを書いてみよう、というのが事の経緯です。
注意点として書き出したのは、
https://teratail.com/questions/30411
こちらをはじめ、自身で書き出してみようと考えまとめたもの。(このURLで書かれていることが半数以上を占めていますが・・)
コードに関しては、あえてシンプルなものが理想と考え、
https://blog.ver001.com/php-upload-multiple/
から拝借し、注意点を考慮しながら自身で書き加えたものです。
(コード中にあるコメントは自身の覚書のためのものです)
動作確認はしており、拡張子の振り分けをはじめ、きちんと設置したいフォルダに描いている形でリネーム、そしてアップロードが出来るまでは確認していますが、注意点として挙げているものの、対策として不十分なものがあるんじゃないか?と半信半疑だったことも手伝い、その他、手元の環境(というか知識がないため)、"./"などを含んだファイル名など用意できず、注意点として挙げた項目をどれだけ対策できているかアドバイスを仰ぎたいと考えての投稿でした。
回答2件
あなたの回答
tips
プレビュー