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图片上传安全检查清单的主要内容,如果未能解决你的问题,请参考以下文章