2012-03-21 10 views
0

私はしばらくの間、画像ファイルのアップロードセキュリティに関する研究を行ってきましたが、それを解読しているようには見えません。サーバーに保存する前にファイルが実際に画像であるかどうかをチェックすることで、素晴らしいセキュリティをいくつか追加したいと考えています(Valums Ajax Uploader)。許可される唯一の拡張子は.jpg、.png、.gifです。拡張子はもちろんチェックされていますが、GD処理によるイメージファイルの検証を検討しています。私はthisthis postから手がかりを取っているので、この件についてはほとんど分かりません。今のところ、イメージ拡張子を持つランダムファイルを簡単に保存できます。これにより、サーバーを保存することができますが、これを防止します。ここまでは、私がphp.phpファイルのhandleUpload関数に追加したスクリプトを示します。残念なことに、アップロードしたファイル、有効な画像、またはアップロードしたファイルに関係なくエラーが返されます。私の全く新しいことを許してください。画像ファイルチェックセキュリティをValums Ajax Uploaderに追加

  $newIm = @imagecreatefromjpeg($uploadDirectory . $filename . '.' . $ext); 
      $newIm2 = @imagecreatefrompng($uploadDirectory . $filename . '.' . $ext); 
      $newIm3 = @imagecreatefromgif($uploadDirectory . $filename . '.' . $ext); 
      if (!$newIm && !$newIm2 && !$newIm3) { 
       return array('error' => 'File is not an image. Please try again'); 
      }else{ 
       imagedestroy($newIm); 
       imagedestroy($newim2); 
       imagedestroy($newIm3); 
} 

これは私のphp.phpファイルのほとんどです。ちなみに、私のファイルは、通常のフォームを使っていますが、デフォルトのアップローダーで提出されていません。誰かが私が間違ってやっていることについて、正しい方向に私を指すことができれば、それは最も

class qqUploadedFileXhr { 
     /** 
     * Save the file to the specified path 
     * @return boolean TRUE on success 
     */ 
     function save($path) {  
      $input = fopen("php://input", "r"); 
      $temp = tmpfile(); 
      $realSize = stream_copy_to_stream($input, $temp); 
      fclose($input); 

      if ($realSize != $this->getSize()){    
       return false; 
      } 

      $target = fopen($path, "w");   
      fseek($temp, 0, SEEK_SET); 
      stream_copy_to_stream($temp, $target); 
      fclose($target); 

      return true; 
     } 
     function getName() { 
      return $_GET['qqfile']; 
     } 
     function getSize() { 
      if (isset($_SERVER["CONTENT_LENGTH"])){ 
       return (int)$_SERVER["CONTENT_LENGTH"];    
      } else { 
       throw new Exception('Getting content length is not supported.'); 
      }  
     } 
    } 

    /** 
    * Handle file uploads via regular form post (uses the $_FILES array) 
    */ 
    class qqUploadedFileForm { 
     /** 
     * Save the file to the specified path 
     * @return boolean TRUE on success 
     */ 
     function save($path) { 
      if(!move_uploaded_file($_FILES['qqfile']['tmp_name'], $path)){ 
       return false; 
      } 
      return true; 
     } 
     function getName() { 
      return $_FILES['qqfile']['name']; 
     } 
     function getSize() { 
      return $_FILES['qqfile']['size']; 
     } 

    } 

    class qqFileUploader { 
     private $allowedExtensions = array(); 
     private $sizeLimit = 2097152; 
     private $file; 

     function __construct(array $allowedExtensions = array(), $sizeLimit = 2097152){   
      $allowedExtensions = array_map("strtolower", $allowedExtensions); 

      $this->allowedExtensions = $allowedExtensions;   
      $this->sizeLimit = $sizeLimit; 

      $this->checkServerSettings();  

      if (isset($_GET['qqfile'])) { 
       $this->file = new qqUploadedFileXhr(); 
      } elseif (isset($_FILES['qqfile'])) { 
       $this->file = new qqUploadedFileForm(); 
      } else { 
       $this->file = false; 
      } 
     } 

     private function checkServerSettings(){   
      $postSize = $this->toBytes(ini_get('post_max_size')); 
      $uploadSize = $this->toBytes(ini_get('upload_max_filesize'));   

      if ($postSize < $this->sizeLimit || $uploadSize < $this->sizeLimit){ 
       $size = max(1, $this->sizeLimit/1024/1024) . 'M';    
       die("{'error':'increase post_max_size and upload_max_filesize to $size'}");  
      }   
     } 

     private function toBytes($str){ 
      $val = trim($str); 
      $last = strtolower($str[strlen($str)-1]); 
      switch($last) { 
       case 'g': $val *= (1024 * 1024 * 1024); 
       case 'm': $val *= (1024 * 1024); 
       case 'k': $val *= 1024;   
      } 
      return $val; 
     } 

     /** 
     * Returns array('success'=>true) or array('error'=>'error message') 
     */ 
     function handleUpload($uploadDirectory, $replaceOldFile = FALSE){ 
      if (!is_writable($uploadDirectory)){ 
       return array('error' => "Server error. Upload directory isn't writable."); 
      } 

      if (!$this->file){ 
       return array('error' => 'No files were uploaded.'); 
      } 

      $size = $this->file->getSize(); 

      if ($size == 0) { 
       return array('error' => 'File is empty'); 
      } 

      if ($size > $this->sizeLimit) { 
       return array('error' => 'File is too large, please upload files that are less than 2MB'); 
      } 

      $pathinfo = pathinfo($this->file->getName()); 
      $filename = $pathinfo['filename']; 
      //$filename = md5(uniqid()); 
      $ext = $pathinfo['extension']; 

      if($this->allowedExtensions && !in_array(strtolower($ext), $this->allowedExtensions)){ 
       $these = implode(', ', $this->allowedExtensions); 
       return array('error' => 'File has an invalid extension, it should be one of '. $these . '.'); 
      } 

      if(!$replaceOldFile){ 
       /// don't overwrite previous files that were uploaded 
       while (file_exists($uploadDirectory . $filename . '.' . $ext)) { 
        $filename .= rand(10, 99); 
       } 
      } 
      $newIm = @imagecreatefromjpeg($uploadDirectory . $filename . '.' . $ext); 
      $newIm2 = @imagecreatefrompng($uploadDirectory . $filename . '.' . $ext); 
      $newIm3 = @imagecreatefromgif($uploadDirectory . $filename . '.' . $ext); 
      if (!$newIm && !$newIm2 && !$newIm3) { 
       return array('error' => 'File is not an image. Please try again'); 
      }else{ 
       imagedestroy($newIm); 
       imagedestroy($newim2); 
       imagedestroy($newIm3); 
} 

      if ($this->file->save($uploadDirectory . $filename . '.' . $ext)){ 
       // At this point you could use $result to do resizing of images or similar operations 
      if(strtolower($ext) == 'jpg' || strtolower($ext) == 'jpeg' || strtolower($ext) == 'gif' || strtolower($ext) == 'png'){ 

       $imgSize=getimagesize($uploadDirectory . $filename . '.' . $ext); 

       if($imgSize[0] > 100 || $imgSize[1]> 100){ 
       $thumbcheck = make_thumb($uploadDirectory . $filename . '.' . $ext,$uploadDirectory . "thumbs/" . $filename ."_thmb" . '.' . $ext,100,100); 
        if($thumbcheck == "true"){ 
         $thumbnailPath = $uploadDirectory . "thumbs/" . $filename ."_thmb". '.' . $ext; 
        } 
       }else{ 
       $this->file->save($uploadDirectory . "thumbs/" . $filename ."_thmb" . '.' . $ext); 
       $thumbnailPath = $uploadDirectory . "thumbs/" . $filename ."_thmb". '.' . $ext; 
       } 

       if($imgSize[0] > 500 || $imgSize[1] > 500){ 
       resize_orig($uploadDirectory . $filename . '.' . $ext,$uploadDirectory . $filename . '.' . $ext,500,500); 
       $imgPath = $uploadDirectory . $filename . '.' . $ext; 
       $newsize = getimagesize($imgPath); 
       $imgWidth = ($newsize[0]+30); 
       $imgHeight = ($newsize[1]+50); 
       }else{ 
       $imgPath = $uploadDirectory . $filename . '.' . $ext; 
       $newsize = getimagesize($imgPath); 
       $imgWidth = ($newsize[0]+30); 
       $imgHeight = ($newsize[1]+50); 
       } 

      } 
       return array('success'=>true, 
       'thumbnailPath'=>$thumbnailPath, 
       'imgPath'=>$imgPath, 
       'imgWidth'=>$imgWidth, 
       'imgHeight'=>$imgHeight 
       ); 
      } else { 
       return array('error'=> 'Could not save uploaded file.' . 
        'The upload was cancelled, or server error encountered'); 
      } 

     }  
    } 

    // list of valid extensions, ex. array("jpeg", "xml", "bmp") 
    $allowedExtensions = array(); 
    // max file size in bytes 
    $sizeLimit = 2097152; 

    $uploader = new qqFileUploader($allowedExtensions, $sizeLimit); 
    $result = $uploader->handleUpload('uploads/'); 

    // to pass data through iframe you will need to encode all html tags 
    echo htmlspecialchars(json_encode($result), ENT_NOQUOTES); 

をいただければ幸いです。

編集

おかげマークB助けと明確化のため。したがって、新しいコード(古いものと同じ場所に貼り付けたhandleUpload関数)は、何をアップロードしてもエラーを投げているので、別の場所に置く必要があるのだろうかと思います。それをどこに置くか確かめてください。

$newIm = getimagesize($uploadDirectory . $filename . '.' . $ext); 
      if ($newIm === FALSE) { 
       return array('error' => 'File is not an image. Please try again'); 

     } 

第二編集:

私はまだそれを実装しようと、the answer is hereを信じています。

+0

アクセスしようとしているファイルパスが何であるかを確認してください。指定されたパス/ファイルが存在しないか、または読み取れない場合は、getimagesizeもfalseを返します。しかし、あなたはnewimgを破壊しません。 getimagesizeは、GDハンドルではなく、イメージに関する情報の配列を返します。 –

+0

私が作っている間違いは狂っている、私はかなり眠っている。 2番目の部分が削除されても、それでもエラーはスローされます。要点は、サーバーに保存される前にこれを実行したいので、パスはまだ存在しません。保存されたファイルではなく、アップロードされたファイルをチェックしたいと思います。私はそれをどのように行うべきか、それをコードのどこに置くべきかわかりません。イメージのサイズを確認するには、保存する必要がありますか? – seaofinformation

+0

このリンクからの回答を見つけました:http://stackoverflow.com/questions/8209646/getimagesize-on-stream-instead-of-string私ができるとそれを投稿します(数時間後に私自身のqに答えることができます)。あなたの助けをもう一度ありがとう。 :) – seaofinformation

答えて

0

魔法のように動作detailed here答えは、私が投稿する前にそれを見ていた希望します。私はそれを正しく実装することを願っています...私はそれを持っているように、それは動作します!

class qqUploadedFileXhr { 
    /** 
    * Save the file to the specified path 
    * @return boolean TRUE on success 
    */ 
function save($path) { 
// Store the file in tmp dir, to validate it before storing it in destination dir 
$input = fopen('php://input', 'r'); 
$tmpPath = tempnam(sys_get_temp_dir(), 'upload'); // upl is 3-letter prefix for upload 
$tmpStream = fopen($tmpPath, 'w'); // For writing it to tmp dir 
$realSize = stream_copy_to_stream($input, $tmpStream); 
fclose($input); 
fclose($tmpStream); 

if ($realSize != $this->getSize()){    
      return false; 
     } 
$newIm = getimagesize($tmpPath); 
if ($newIm === FALSE) { 
    return false; 

}else{  
// Store the file in destination dir, after validation 
$pathToFile = $path . $filename; 
$destination = fopen($pathToFile, 'w'); 
$tmpStream = fopen($tmpPath, 'r'); // For reading it from tmp dir 
stream_copy_to_stream($tmpStream, $destination); 
fclose($destination); 
fclose($tmpStream); 

     return true; 
} 
    } 
    function getName() { 
     return $_GET['qqfile']; 
    } 
    function getSize() { 
     if (isset($_SERVER["CONTENT_LENGTH"])){ 
      return (int)$_SERVER["CONTENT_LENGTH"];    
     } else { 
      throw new Exception('Getting content length is not supported.'); 
     }  
    } 
} 
0

だけgetimagesize()を使用します。

$newIm = getimagesize$uploadDirectory . $filename . '.' . $ext'); 
if ($newIm === FALSE) { 
    die("Hey! what are you trying to pull?"); 
} 

あなたのコードに問題が3 imagecreateコールとそれに続く場合は()文です。それはOR句として書かれているはずです。 "いずれかの画像読み込みの試行が失敗した場合は、それに続いて不平を言います"。あなたのものは「すべての画像の試行が失敗した場合」と書かれています.gif/jpg/pngが実際にアップロードされた場合は不可能です。

+0

私はいくつかのnoobコードミスをしていると思った。 :)ありがとう。私はgetimagesizeをすることを考えましたが、私はこれを読んでいます - http://ha.ckers.org/blog/20070604/passing-malicious-php-through-getimagesize/。その記事で話していることを心配してはいけませんか? – seaofinformation

+0

これは問題ではありますが、アップロードしたファイルに直接アクセスしたり、適切なファイル名でファイルを保存していないかどうかは問題ではありません。 'pic.gif'として保存すると、PHPインタプリタを介して実行されず、宇宙の中で最も悪質なコードが実行されませんでした。 –

+0

ありがとうございました。私の質問を更新しましたが、毎回エラーを投げかけています。 – seaofinformation

関連する問題