PHP图片上传安全检查清单

Posted

技术标签:

【中文标题】PHP图片上传安全检查清单【英文标题】:PHP image upload security check list 【发布时间】: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 的许可证 :) 【参考方案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" >

在上传前还可以使用 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 攻击。

攻击场景:

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

    攻击者通过邮件、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 网站。我认为适用的三重奏是那些扩展:

    GD(6 个漏洞) ImageMagick(44 个漏洞) Gmagick(12 个漏洞)

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

【讨论】:

【参考方案9】:

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

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

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

    设置一个单独的虚拟主机来提供从不执行 PHP、Perl 等脚本的静态内容。 将文件上传到另一台服务器(例如便宜的 VPS、Amazon S3 等)。 将它们保存在同一台服务器上,并使用 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

【讨论】:

以上是关于PHP图片上传安全检查清单的主要内容,如果未能解决你的问题,请参考以下文章

php为啥上传图片会失败

使用 PHP 安全上传图片

文件上传图片

图片上传太慢php

php图片上传

使用JAVA如何对图片进行格式检查以及安全检查处理