PHP图像上传安全检查列表
我正在编写脚本将图像上传到我的应用程序。 以下安全措施是否足以使应用程序从脚本端安全?
- 使用.httaccess禁用PHP在上传文件夹内运行。
- 如果文件名包含string“php”,则不允许上传。
- 只允许扩展名:jpg,jpeg,gif和png。
- 只允许图像文件types。
- 不允许使用两种文件types的图像。
- 更改图像名称。
- 上传到不是根目录的子目录。
这是我的脚本:
$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("Ymd").'/' ; 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"; }
欢迎任何新的提示:)
使用GD(或Imagick)重新处理图像并保存处理后的图像。 所有其他人对黑客来说只是有趣的无聊。
编辑:正如rr指出的,使用move_uploaded_file()
进行上传。
延迟编辑:顺便说一句,你想对你的上传文件夹非常严格。 这些地方是许多攻击发生的黑暗angular落之一。 这适用于任何types的上传和任何编程语言/服务器。 检查https://www.owasp.org/index.php/Unrestricted_File_Upload
对于图像文件的安全testing,我可以考虑4级证券。 他们将是:
- 等级1:检查分机(分机文件结尾)
- 级别2:检查MIMEtypes
($file_info = getimagesize($_FILES['image_file']; $file_mime = $file_info['mime'];)
- 等级3:读取前100个字节,并检查它们是否在以下范围内有任何字节:ASCII 0-8,12-31(十进制)。
- 等级4:检查标题中的幻数(文件的前10-20个字节)。 你可以从这里find一些文件头字节: http : //en.wikipedia.org/wiki/Magic_number_%28programming%29#Examples
注意:加载整个图像会很慢。
XSS警告
还有一个非常重要的说法。 不要在浏览器中提供/上传任何可以解释为HTML的内容。
由于这些文件位于您的域名中,因此该HTML文档中包含的JavaScript将可以访问您的所有Cookie,从而实现某种XSS攻击。
攻击场景:
-
攻击者用JS代码上传HTML文件,将所有的cookies发送到他的服务器。
-
攻击者通过邮件,下午或者通过他或者任何其他站点上的iframe发送链接给你的用户。
最安全的解决scheme
使上传的内容仅在子域或其他域上可用。 这样cookies不会被访问。 这也是谷歌的性能提示之一:
https://developers.google.com/speed/docs/best-practices/request#ServeFromCookielessDomain
您也可以在$ _FILES ['my_files'] ['tmp_name']上运行“is_uploaded_file”。 见http://php.net/manual/en/function.is-uploaded-file.php
在上传目录中创build一个新的.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
只要确保重命名file upload+忘记检查types,内容等
我会重复一些我在相关问题上发表的内容。
您可以使用Fileinfo函数 (以前版本的PHP中的mime_content_type())检测内容types。
在旧的Mimetype扩展中,摘录了PHP手册,现在被Fileinfo取代:
本模块中的函数通过在文件中的特定位置查找某些魔法字节序列来尝试猜测文件的内容types和编码。 虽然这不是一个防弹的办法,启发式使用做了很好的工作。
getimagesize()
也可以做得很好,但是其他大部分的检查都是无稽之谈。 例如,为什么stringphp
不允许在文件名中。 你不打算在PHP脚本中包含图像文件,只是因为它的名称包含php
string,是吗?
当涉及到重新创build图像,在大多数情况下,它会提高安全性,直到你使用的图书馆不容易。
那么哪个PHP扩展最适合安全的图像重新创build? 我检查过CVE详细信息网站。 我认为适用的三人是那些扩展名:
- GD (6个漏洞)
- ImageMagick (44个漏洞)
- Gmagick (12个漏洞)
从比较中我认为GD最适合,因为它具有最less的安全问题,而且相当老旧。 其中三个是至关重要的,但ImagMagick和Gmagick没有更好的performance… ImageMagick似乎是非常错误(至less在安全方面),所以我selectGmagick作为第二个选项。
如果安全是非常重要的使用数据库来保存文件名和重命名文件名,在这里你可以改变文件的扩展名为.myfile的东西,并制作一个PHP文件的头像发送图像。 PHP可以更安全,你可以像img标签一样使用它:
<img src="send_img.php?id=555" alt="">
上传之前,还请使用EXIF检查文件扩展名。
允许用户以PHP安全地上传文件的最简单答案是: 始终将文件保存在文档根目录之外。
例如:如果您的文档根目录是/home/example/public_html
,则将文件保存到/home/example/uploaded
。
使您的文件安全地脱离由您的Web服务器直接执行的范围,有几种方法仍然可以使访问者访问它们:
- 设置一个单独的虚拟主机,用于提供从不执行PHP,Perl等脚本的静态内容。
- 上传文件到另一台服务器(例如便宜的VPS,亚马逊S3等)。
- 将它们保留在同一台服务器上,并使用PHP脚本代理请求,以确保文件只能读取,不可执行。
但是,如果您使用此列表中的选项1或3,并且您的应用程序中存在本地文件包含漏洞,则您的文件上载表单仍然可能成为攻击媒介 。
当用户上传图片时,保持网站安全的最佳方法是执行以下步骤:
- 检查图像扩展名
- 用这个函数“getimagesize()”检查图像大小
- 之后你可以使用函数“file_get_contents()”
- 最后,你应该插入file_Content到你的数据库我认为这是最好的方法! 你有什么看法?
对于图像文件,您也可以在重命名后更改文件权限,以确保它永远不会执行(rw-r – r–)
我正在使用php-upload-script为每个上传的文件创build一个新的随机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); ?>