PHP图像上传安全检查列表

时间:2010-11-12 16:24:39

标签: php security upload

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

  • 使用.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";
  }

欢迎任何新的提示:)

12 个答案:

答案 0 :(得分:33)

使用GD(或Imagick)重新处理图像并保存处理后的图像。所有其他人都只是<罢工>有趣对黑客无聊。

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

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

答案 1 :(得分:12)

对于图像文件的安全性测试,我可以想到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

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

答案 2 :(得分:7)

XSS警告

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

由于文件位于您的域中,因此该HTML文档中包含的javascript将可以访问您的所有Cookie,从而实现某种XSS攻击。

攻击情形:

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

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

  3. 最安全的解决方案:

    使上传的内容仅在子域或另一个域上可用。这样就无法访问cookie。这也是谷歌的性能提示之一:

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

答案 3 :(得分:6)

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

答案 4 :(得分:6)

在uploads目录中创建一个新的.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

请确保重命名您上传的文件+忘记检查类型,内容等

答案 5 :(得分:5)

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

您可以使用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个漏洞)
  4. 从比较中我认为GD最适合,因为它的安全问题最少,而且它们已经很老了。其中三个是关键,但ImagMagick和Gmagick表现不佳...... ImageMagick似乎非常错(至少在安全方面),所以我选择Gmagick作为第二选择。

答案 6 :(得分:2)

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

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

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

答案 7 :(得分:2)

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

例如:如果您的文档根目录是1. Go to Azure VM (manage.windowsazure.com), and add 2 endpoints: 1. Name: FTP (Protocol TCP, Public Port 21, Private Port 21) 2. Name: FTP Passive (Protocol TCP, Public Port 60000, Private Port 60000) 2. Go back to VM (via RDP), Open connection for port 21, and 60000 on Windows Firewall inbound rule. 3. Download and open FileZilla Server. 4. Click Edit -> Users and add user and shared directory as needed. 5. Click Edit -> Settings. On the sidebar click Passive Mode Settings. 6. Check "use custom port range" and enter 60000 - 60000 7. On ip4specific part, select radio button "use the following ip", and enter your xxxx.cloudapp.net. 8. Save, and run the server. That's it, you can now connect to FTP from outside of VM. ,请将文件保存到/home/example/public_html

您的文件安全地超出了由您的网络服务器直接执行的范围,您可以通过以下几种方式让访问者访问它们:

  1. 设置一个单独的虚拟主机,用于提供永远不会执行PHP,Perl等脚本的静态内容。
  2. 将文件上传到另一台服务器(例如便宜的VPS,Amazon S3等)。
  3. 将它们保存在同一台服务器上,并使用PHP脚本代理请求,以确保该文件只能是可读的,而不是可执行的。
  4. 但是,如果您使用此列表中的选项1或3并且您的应用程序中存在本地文件包含漏洞,则您的文件上载表单可以still be an attack vector

答案 8 :(得分:2)

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

  • 检查图片扩展程序
  • 使用此功能检查图像大小&#34; getimagesize()&#34;
  • 之后你可以使用函数&#34; file_get_contents()&#34;
  • 最后,您应该将file_Content插入数据库 我认为这是最好的方式!你的意见是什么?

答案 9 :(得分:1)

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

br

答案 10 :(得分:0)

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

答案 11 :(得分:0)

我正在使用一个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);
        ?>