Web安全 - 文件上传漏洞

  • 作者:KK

  • 发表日期:2017.9.20


要点速读

  1. 当上传文件的逻辑没有判断文件类型,于是就可以上传一个.php等服务端可执行的文件,上传后通过网址访问这个脚本就成功在服务器上执行了自己想要的代码

  2. 单纯判断上传文件的后缀名没有实际意义,至少要判断MimeType


上传可执行文件

Web应用里通常都有头像上传、文章图片上传、个人相册照片上传等图片提交入口,这些地方如果没有限制上传文件的类型,那就可以恶意上传.exe.php.js.asp.sh

如果说上传后地址是http://xxx.com/upload/a.jpg那换成可执行文件就变成了http://xxx.com/upload/a.php,于是直接访问这个网址,PHP代码就执行起来了,要读数据库、查文件系统、删除文件什么的都能天马行空。


有效预防1:MimeType检测

服务端逻辑要判断文件的后缀名、MimeType类型,只允许特定的进行上传,其它全部拒绝,另外,前端也要意思意思一下,对选择文件的后缀进行限制

这里附一下PHP的MimeType检测示例(要安装fileinfo扩展):

$uploadFile = __DIR__ . '/test.jpg';

$infoHandler = finfo_open(FILEINFO_MIME_TYPE);
$mimeType = finfo_file($infoHandler, $uploadFile);
finfo_close($infoHandler);

echo $mimeType;	// image/jpeg

if(!in_array($mimeType, [
	'image/jpeg',
	'image/png',
	'image/gif',
])){
	exit('非法的文件类型');
}

无论你加不加其它什么检测辅助,至少要用这个MimeType检测


有效预防2:禁止执行

上传文件的保存目录设置为不执行脚本,就是那个/upload目录,比如在Apache下是这样配置的:

<Directory "/data/www/project/upload">
	<FilesMatch "\.(?i:php|php3|php4)$">
		Order Allow,Deny
		Deny from all
	</FilesMatch>
</Directory>

这样当请求地址指定文件为 .php 这样的时候就会禁止访问,其中要注意到上面FilesMatch的表达式前面有个?i,表示不区分大小写,否则别人将后缀改成.PHP也能执行


有效预防3:设置独立的资源存储服务器

比如你可以看到有些大厂是这样的:img.qq.comimg.sina.comphoto.taobao.com,毕竟大部分上传就是上传图片,他们把图片储存独立开去

客观地说自然有为应用服务器减压的功能,但其实也有安全的效果,因为当图片里包含恶意脚本一旦不小心被执行的时候,会由于浏览器的同源策略导致它们执行失败


有效预防4:上传后重命名

其实这个不说你也会重命名,光是上传同名文件会覆盖的问题就要逼着你重命名了。这里只是说,如果人家上传的是.htaccesscrossdomain.xml等有特定效果的文件名,会被服务器特殊处理的文件名,只要我们在上传后重命名文件为随机数或字符串等,就不是那个名字了,那它就算成功钻了空子进了服务器也不容易生效


有效预防5:不要随便在后台提供重命名操作

有些CMS的图片资源管理很厉害,能在后台管理各个上传文件,包含重命名这些文件,这就为一些懂技术的人操作后台提供了钻空子的可能,他只要找到漏洞上传一张xxx.gif,里面实际上是可执行脚本,然后再在后台重命名为xxx.php之类的就行了


无效预防1:判断请求来源的Content-type

其实HTTP请求的Content-type只是向服务器说明“我这次发过来的数据是怎样的数据”,但实际上报文体里的数据可以不是那个数据,因此Content-type: image/jpeg的时候,那报文体里面可能是一段PHP脚本,因此信不过(菜鸟要对HTTP协议有个初步的了解)


无效预防2:判断文件头

有些文件的开头总是统一的标识,比如gif图片开头总是GIF89a

可是别人也能将文件内容改成这样来伪装gif图片

GIF89a<?php phpinfo();

所以这招也不管用


无效预防3:判断文件名后缀名

这也很简单啦,后缀名是.gif但内容是php脚本嘛,不过一般我们还是会加这个判断,这是因为有时候前端工程师不一定注意到这个问题,会遗漏了文件后缀的限制,于是用户不小心选错文件就会提交到后端了,始终不是个好事情,所以还是判断判断吧,我觉得这是个问题高发区