【问题标题】:PHP image upload security check listPHP图片上传安全检查清单
【发布时间】:2011-05-09 04:21:34
【问题描述】:

我正在编写一个脚本来将图像上传到我的应用程序。以下安全步骤是否足以使应用程序在脚本方面安全?

  • 使用 .httaccess 禁止 PHP 在上传文件夹中运行。
  • 如果文件名包含字符串“php”,则不允许上传。
  • 只允许扩展名:jpg、jpeg、gif 和 png。
  • 只允许图像文件类型。
  • 禁止使用两种文件类型的图像。
  • 更改图片名称。
  • 上传到子目录而不是根目录。

这是我的脚本:

 $filename=$_FILES['my_files']['name'];
 $filetype=$_FILES['my_files']['type'];
 $filename = strtolower($filename);
 $filetype = strtolower($filetype);

 //check if contain php and kill it 
 $pos = strpos($filename,'php');
 if(!($pos === false)) {
  die('error');
 }




 //get the file ext

 $file_ext = strrchr($filename, '.');


 //check if its allowed or not
 $whitelist = array(".jpg",".jpeg",".gif",".png"); 
 if (!(in_array($file_ext, $whitelist))) {
    die('not allowed extension,please upload images only');
 }


 //check upload type
 $pos = strpos($filetype,'image');
 if($pos === false) {
  die('error 1');
 }
 $imageinfo = getimagesize($_FILES['my_files']['tmp_name']);
 if($imageinfo['mime'] != 'image/gif' && $imageinfo['mime'] != 'image/jpeg'&& $imageinfo['mime']      != 'image/jpg'&& $imageinfo['mime'] != 'image/png') {
   die('error 2');
 }
//check double file type (image with comment)
if(substr_count($filetype, '/')>1){
die('error 3')
}

 // upload to upload direcory 
 $uploaddir = 'upload/'.date("Y-m-d").'/' ;

if (file_exists($uploaddir)) {  
} else {  
    mkdir( $uploaddir, 0777);  
}  
  //change the image name
 $uploadfile = $uploaddir . md5(basename($_FILES['my_files']['name'])).$file_ext;



  if (move_uploaded_file($_FILES['my_files']['tmp_name'], $uploadfile)) {
 echo "<img id=\"upload_id\" src=\"".$uploadfile."\"><br />";
  } else {
   echo "error";
  }

欢迎任何新的提示:)

【问题讨论】:

  • 我会删除以下规则:如果文件名包含字符串“php”,则不允许上传。不需要,因为您正在重命名文件。
  • 你可以从github下载Secure Image upload。它是目前最安全的 PHP 脚本。它也支持图像调整大小/裁剪。
  • @Alez 快速浏览该类,我能看到的唯一安全性是扩展检查。请,请说不是这样!
  • @Fricker 不是这样。凭什么? pathinfo(, PATHINFO_EXTENSION) 是一种获得最准确文件扩展名的非常可靠的方法,实际上没有比这更可靠的了。阅读上面写着"note"
  • @Alez ofc & btw 我喜欢 Luke 3:11 的许可证 :)

标签: php security upload


【解决方案1】:

您可能还想在 $_FILES['my_files']['tmp_name'] 上运行“is_uploaded_file”。见http://php.net/manual/en/function.is-uploaded-file.php

【讨论】:

    【解决方案2】:

    对于图像文件的安全测试,我可以考虑 4 级证券。他们将是:

    • 级别 1:检查扩展名(扩展名文件以结尾)
    • 2 级:检查 MIME 类型($file_info = getimagesize($_FILES['image_file']; $file_mime = $file_info['mime'];)
    • 级别 3:读取前 100 个字节并检查它们是否有以下范围内的字节:ASCII 0-8、12-31(十进制)。
    • 级别 4:检查标头中的幻数(文件的前 10-20 个字节)。您可以从这里找到一些文件头字节:http://en.wikipedia.org/wiki/Magic_number_%28programming%29#Examples

    注意:加载整个图像会很慢。

    【讨论】:

    • 3 & 4 不会特别慢。将整个图像加载到库中以进行验证会很慢。
    【解决方案3】:

    使用 GD(或 Imagick)重新处理图像并保存处理后的图像。对于黑客来说,其他所有的只是fun无聊。

    编辑:正如 rr 指出的那样,使用move_uploaded_file() 进行任何上传。

    后期编辑:顺便说一句,您希望对上传文件夹进行非常严格的限制。这些地方是许多漏洞发生的黑暗角落之一。这适用于任何类型的上传和任何编程语言/服务器。检查https://www.owasp.org/index.php/Unrestricted_File_Upload

    【讨论】:

    • +1。如果 它是图像 并且包含有效的扩展名,我看不出列表中的任何检查(在问题中)如何有用。
    • 我不添加这个,因为我认为它需要大量的 CPU 处理。
    • 请注意我的话:大多数时候,CPU 是最冗余的资源。只需观看任何给定机器/服务器的图表(可能除了 Google、Facebook 等服务器)。你会看到它大部分时间都在除尘。 SETI@Home 之类的项目试图利用这些空闲时间。无论如何,为增加安全性付出努力是值得的。
    • 您愿意举个“重新处理”的例子吗?让我们说 imagick(或者更糟糕的 GD)?
    • @thorne51,我相信没有任何功能足以进行检查。所以根本不检查,只需创建一个新图像。示例:PHP 代码可以隐藏在图像的 EXIF、XMP 等部分中,并且函数/程序可能会失败(无意中或通过使用漏洞代码)报告该代码。
    【解决方案4】:

    如果安全性非常重要,请使用数据库保存文件名和重命名文件名,在这里您可以将文件扩展名更改为 .myfile 之类的东西,并制作一个 php 文件以发送带有标题的图像。 php 可以更安全,你可以在 img 标签中使用它,比如 blow :

    <img src="send_img.php?id=555" alt="">
    

    在上传前还可以使用 EXIF 检查文件扩展名。

    【讨论】:

    • 如果您使用数据库,那么您应该确保首先清理文件名。
    【解决方案5】:

    对于图像文件,您还可以在重命名后更改文件权限以确保它永远不会执行(rw-r--r--)

    【讨论】:

    • -1。首先:X(执行)标志不受客户端控制,无论如何都没有设置。第二:PHP 引擎不要求文件有 X 标志才能执行。
    【解决方案6】:

    在上传目录中创建一个新的 .htaccess 文件并粘贴此代码:

    php_flag engine 0
    RemoveHandler .phtml .php .php3 .php4 .php5 .php6 .phps .cgi .exe .pl .asp .aspx .shtml .shtm .fcgi .fpl .jsp .htm .html .wml
    AddType application/x-httpd-php-source .phtml .php .php3 .php4 .php5 .php6 .phps .cgi .exe .pl .asp .aspx .shtml .shtm .fcgi .fpl .jsp .htm .html .wml
    

    请务必重命名您上传的文件 + 忘记检查类型、内容等

    【讨论】:

    • 你确定吗?没有在图像发布数据中再次检查 php 脚本?
    【解决方案7】:

    XSS 警告

    还有一个非常重要的评论。不要在浏览器中提供/上传任何可能被解释为 HTML 的内容。

    由于文件位于您的域中,该 HTML 文档中包含的 javascript 将可以访问您的所有 cookie,从而启用某种 XSS 攻击。

    攻击场景:

    1. 攻击者上传带有 JS 代码的 HTML 文件,将所有 cookie 发送到他的服务器。

    2. 攻击者通过邮件、PM 或简单地通过他或任何其他网站上的 iframe 将链接发送给您的用户。

    最安全的解决方案:

    使上传的内容仅在子域或其他域中可用。这样 cookie 将无法访问。这也是google的性能提示之一:

    https://developers.google.com/speed/docs/best-practices/request#ServeFromCookielessDomain

    【讨论】:

      【解决方案8】:

      我将重复我在相关问题中发布的内容。

      您可以使用Fileinfo functions 检测内容类型(在以前的 PHP 版本中为 mime_content_type())。

      旧的 Mimetype 扩展的 PHP 手册摘录,现在被 Fileinfo 取代:

      这个模块中的函数尝试猜测内容类型和 通过在以下位置查找某些魔术字节序列来编码文件 文件中的特定位置。虽然这不是防弹的 使用的启发式方法做得很好。

      getimagesize() 也可能做得很好,但是您执行的大多数其他检查都是无稽之谈。例如为什么文件名中不允许使用字符串php。您不会在 PHP 脚本中包含图像文件,因为它的名称包含 php 字符串,是吗?


      当涉及到重新创建图像时,在大多数情况下它会提高安全性...直到您使用的库不存在漏洞。

      那么,哪种 PHP 扩展最适合安全地重新创建图像?我检查了CVE details 网站。我认为适用的三重奏是那些扩展:

      1. GD(6 个漏洞)
      2. ImageMagick(44 个漏洞)
      3. Gmagick(12 个漏洞)

      从比较中我认为 GD 最适合,因为它的安全问题数量最少,而且它们很老。其中三个很关键,但 ImagMagick 和 Gmagick 并没有表现得更好...... ImageMagick 似乎有很多错误(至少在安全性方面),所以我选择 Gmagick 作为第二个选项。

      【讨论】:

        【解决方案9】:

        allow users to securely upload files in PHP 的最简单答案是:始终将文件保存在文档根目录之外。

        例如:如果您的文档根目录是/home/example/public_html,则将文件保存到/home/example/uploaded

        由于您的文件不受网络服务器直接执行的限制,您仍然可以通过多种方式让访问者访问它们:

        1. 设置一个单独的虚拟主机来提供从不执行 PHP、Perl 等脚本的静态内容。
        2. 将文件上传到另一台服务器(例如便宜的 VPS、Amazon S3 等)。
        3. 将它们保存在同一台服务器上,并使用 PHP 脚本代理请求,以确保文件只能读取,不能执行。

        但是,如果您使用此列表中的选项 1 或 3,并且您的应用程序中存在本地文件包含漏洞,则您的文件上传表单可以still be an attack vector

        【讨论】:

          【解决方案10】:

          在用户上传图片时确保您的网站安全的最佳方式是执行以下步骤:

          • 检查图片扩展名
          • 使用此函数“getimagesize()”检查图像大小
          • 之后您可以使用函数“file_get_contents()”
          • 最后您应该将 file_Content 插入到您的数据库中 我认为这是最好的方法!你有什么看法?

          【讨论】:

            【解决方案11】:

            我正在使用 php-upload-script 为每个上传的文件创建一个新的随机 4 字节数,然后用这 4 个字节对文件内容进行异或(根据需要重复它们),最后在保存之前将 4 个字节附加到文件中。

            为了下载,必须再次将文件的4个字节剪掉,内容将再次与它们进行异或运算并将结果发送给客户端。

            这样,我可以确定我保存在服务器上的文件不会是可执行的,或者对任何应用程序都没有任何潜在的意义。另外,我不需要任何额外的数据库来存储文件名。

            这是我为此使用的代码:

            上传:

                    <?php
                        $outputfilename = $_POST['filename'];
                        $inputfile = $_FILES["myblob"]["tmp_name"];
                        $tempfilename="temp.tmp";
            
                        if( move_uploaded_file($inputfile, $tempfilename) ) {
                            $XORstring = random_bytes(4);
            
                            $tempfile=fopen($tempfilename, "r");
                            $outputfile=fopen($outputfilename, "w+");
                            flock($outputfilename, LOCK_EX);
            
                            fwrite($outputfilename, $XORbytes1);
            
                            while ( $buffer = fread($tempfile, 4) ) {
                                $buffer = $buffer ^ $XORstring;
                                fwrite($outputfilename, $buffer);
                            }
            
                            flock($outputfilename, LOCK_UN);
            
                            fclose($tempfile);
                            fclose($outputfile);
            
                            unlink($tempfilename);
                        }
            
                        exit(0);
                    ?>
            

            下载:

                    <?php
                        $inputfilename = $_POST['filename'];
                        $tempfilename = "temp.tmp";
            
                        $inputfile=fopen($inputfilename, "r");
                        $tempfile=fopen($tempfilename, "w+");
                        flock($tempfile, LOCK_EX);
            
                        $XORstring = fread($inputfile, 4);
            
                        while ( $buffer = fread($inputfile, 4) ) {
                            $buffer = $buffer ^ $XORstring;
                            fwrite($tempfile, $buffer);
                        }
            
                        flock($tempfile, LOCK_UN);
            
                        fclose($inputfile);
                        fclose($tempfile);
            
                        readfile($tempfile);
                        unlink($tempfile);
            
                        exit(0);
                    ?>
            

            【讨论】:

              【解决方案12】:

              我昨天寻找最好的安全解决方案,它似乎使用了额外的应用程序,因为 GD 就是其中之一 - 但如果有人没有钱购买好的服务器,它可能会导致服务器响应的巨大延迟。但是,如果您没有图库或某些图像预览并且您只提供下载上传文件的选项,我想出了一些技巧,但只能在一种情况下使用。所以事情:第一。即时更改名称并在压缩文件然后删除源文件之后 - 所以任何人都不可能导致隐藏代码的执行 - 在场外,此解决方案仅对服务器安全 - 导致下载文件的用户打开一些感染了一个等-但在我的情况下,这不是问题。

              br

              【讨论】:

                猜你喜欢
                • 2012-09-18
                • 2013-07-09
                • 1970-01-01
                • 1970-01-01
                • 2023-01-18
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                相关资源
                最近更新 更多