首页 文章

PHP图像上传安全检查列表

提问于
浏览
60

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

  • 使用.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."\">
"; } else { echo "error"; }

欢迎任何新的提示:)

11 回答

  • 12

    使用GD(或Imagick)重新处理图像并保存处理后的图像 . 所有其他人对黑客来说都很有趣 .

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

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

  • 2

    对于图像文件的安全性测试,我可以想到4级证券 . 他们将是:

    • 级别1:检查扩展名(扩展名文件以)结尾

    • 第2级:检查MIME类型 ($file_info = getimagesize($_FILES['image_file']; $file_mime = $file_info['mime'];)

    • Level 3:读取前100个字节并检查它们是否具有以下范围内的任何字节:ASCII 0-8,12-31(十进制) .

    • 等级4:检查 Headers 中的幻数(文件的前10-20个字节) . 你可以从这里找到一些文件头字节:http://en.wikipedia.org/wiki/Magic_number_%28programming%29#Examples

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

  • 0

    XSS警告

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

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

    攻击情形:

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

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

    最安全的解决方案:

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

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

  • 6

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

  • 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

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

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

    旧版Mimetype扩展的PHP手册摘录,现在由Fileinfo替换:

    此模块中的函数尝试通过查找文件中特定位置的某些魔术字节序列来猜测文件的内容类型和编码 . 虽然这不是一种防弹方法,但使用的启发式方法做得非常好 .

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


    在重新创建图像时,在大多数情况下,它将提高安全性......直到您使用的库不易受攻击 .

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

    从比较中我认为GD最适合,因为它具有最少的安全问题并且它们已经很老了 . 其中三个是关键,但ImagMagick和Gmagick表现不佳...... ImageMagick似乎非常错(至少在安全方面),所以我选择Gmagick作为第二选择 .

  • 2

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

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

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

  • 34

    最简单的回答allow users to securely upload files in PHP是: Always save files outside of your document root.

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

    如果您的文件安全地不受Web服务器直接执行的限制,您可以通过以下几种方式让访问者访问它们:

    • 设置一个单独的虚拟主机,用于提供从不执行PHP,Perl等脚本的静态内容 .

    • 将文件上传到另一台服务器(例如便宜的VPS,Amazon S3等) .

    • 将它们保存在同一台服务器上,并使用PHP脚本代理请求,以确保文件只能是可读的,而不是可执行的 .

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

  • 2

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

    • 检查图像扩展名

    • 使用此功能检查图像大小"getimagesize()"

    • 之后你可以使用函数"file_get_contents()"

    • 最后你应该将file_Content插入你的数据库我认为's the best way ! nd what'你的看法?

  • 0

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

  • 7

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

相关问题