你不知道的文件上传漏洞php代码分析


Posted in PHP onSeptember 29, 2016

漏洞描述

开发中文件上传功能很常见,作为开发者,在完成功能的基础上我们一般也要做好安全防护。
文件处理一般包含两项功能,用户上传和展示文件,如上传头像。

文件上传攻击示例

upload.php

<?php
$uploaddir = 'uploads/'; 
$uploadfile = $uploaddir . basename($_FILES['userfile']['name']);
if (move_uploaded_file($_FILES['userfile']['tmp_name'], $uploadfile)){
 echo "File is valid, and was successfully uploaded.\n";
} 
else {
 echo "File uploading failed.\n";
}
?>

upload.html

<form name="upload" action="upload1.php" method="POST" ENCTYPE="multipart/formdata">
Select the file to upload: <input type="file" name="userfile">
<input type="submit" name="upload" value="upload">
</form>

上述代码未经过任何验证,恶意用户可以上传php文件,代码如下

<?php eval($_GET['command']);?>

恶意用户可以通过访问 如http://server/uploads/shell.php?command=phpinfo(); 来执行远程命令

Content-type验证

upload.php

<?php
if($_FILES['userfile']['type'] != "image/gif") {//获取Http请求头信息中ContentType
 echo "Sorry, we only allow uploading GIF images";
 exit;
}
$uploaddir = 'uploads/';
$uploadfile = $uploaddir.basename($_FILES['userfile']['name']);
if (move_uploaded_file($_FILES['userfile']['tmp_name'], $uploadfile)){
 echo "File is valid, and was successfully uploaded.\n";
} else {
 echo "File uploading failed.\n";
}
?>

该方式是通过Http请求头信息进行验证,可通过修改Content-type ==> image/jpg绕过验证,可以通过脚本或BurpSuite、fiddle修改
如下
Content-Disposition: form-data; name="userfile"; filename="shell.php"
Content-Type: image/gif

图片类型验证

该方法通过读取文件头中文件类型信息,获取文件类型

备注:如JPEG/JPG文件头标识为FFD8

你不知道的文件上传漏洞php代码分析

upload.php

<?php
$imageinfo = getimagesize($_FILES['userfile']['tmp_name']);
if($imageinfo['mime'] != 'image/gif' && $imageinfo['mime'] != 'image/jpeg') {
 echo "Sorry, we only accept GIF and JPEG images\n";
 exit;
}
$uploaddir = 'uploads/';
$uploadfile = $uploaddir . basename($_FILES['userfile']['name']);
if (move_uploaded_file($_FILES['userfile']['tmp_name'], $uploadfile)){
 echo "File is valid, and was successfully uploaded.\n";
} else {
 echo "File uploading failed.\n";
}
?>

可以通过图片添加注释来绕过此验证。
如添加注释<?php phpinfo(); ?>,保存图片后将其扩展名改为php,则可成功上传。
上传成功后访问该文件则可看到如下显示

你不知道的文件上传漏洞php代码分析

文件扩展名验证

 通过黑名单或白名单对文件扩展名进行过滤,如下代码

upload.php

<?php
$blacklist = array(".php", ".phtml", ".php3", ".php4");
foreach ($blacklist as $item) {
if(preg_match("/$item\$/i", $_FILES['userfile']['name'])) {
 echo "We do not allow uploading PHP files\n";
 exit;
}
}
$uploaddir = 'uploads/';
$uploadfile = $uploaddir . basename($_FILES['userfile']['name']);
if (move_uploaded_file($_FILES['userfile']['tmp_name'], $uploadfile)){
 echo "File is valid, and was successfully uploaded.\n";
} else {
 echo "File uploading failed.\n";
}
?>

当黑名单不全,构造特殊文件名可以绕过扩展名验证

直接访问上传的文件

将上传文件保存在非web root下其他文件夹下,可以防止用户通过路径直接访问到文件。
upload.php

<?php
$uploaddir = 'd:/uploads/';
$uploadfile = $uploaddir . basename($_FILES['userfile']['name']);
if (move_uploaded_file($_FILES['userfile']['tmp_name'], $uploadfile)) {
echo "File is valid, and was successfully uploaded.\n";
} else {
echo "File uploading failed.\n";
}
?>

用户不可以直接通过http://localhost/uploads/ 来访问文件,必须通过view.php来访问
view.php

<?php
$uploaddir = 'd:/uploads/';
$name = $_GET['name'];
readfile($uploaddir.$name);
?>

查看文件代码未验证文件名,用户可以通过例如http://localhost/view.php?name=..//php/upload.php,查看指定的文件

解决漏洞示例

upload.php

<?php
require_once 'DB.php';
$uploaddir = 'D:/uploads/'; 
$uploadfile = tempnam($uploaddir, "upload_");
if (move_uploaded_file($_FILES['userfile']['tmp_name'], $uploadfile)) {
$db =& DB::connect("mysql://username:password@localhost/database");
if(PEAR::isError($db)) {
unlink($uploadfile);
die "Error connecting to the database";
}
$res = $db->query("INSERT INTO uploads SET name=?, original_name=?,mime_type=?",
array(basename($uploadfile,basename($_FILES['userfile']['name']),$_FILES['userfile']['type']));
if(PEAR::isError($res)) {
unlink($uploadfile);
die "Error saving data to the database. The file was not uploaded";
}
$id = $db->getOne('SELECT LAST_INSERT_ID() FROM uploads');
echo "File is valid, and was successfully uploaded. You can view it <a href=\"view.php?id=$id\">here</a>\n";
} else {
echo "File uploading failed.\n";
}
?>

view.php

<?php
require_once 'DB.php';
$uploaddir = 'D:/uploads/';
$id = $_GET['id'];
if(!is_numeric($id)) {
die("File id must be numeric");
}
$db =& DB::connect("mysql://root@localhost/db");
if(PEAR::isError($db)) {
die("Error connecting to the database");
}
$file = $db->getRow('SELECT name, mime_type FROM uploads WHERE id=?',array($id), DB_FETCHMODE_ASSOC);
if(PEAR::isError($file)) {
die("Error fetching data from the database");
}
if(is_null($file) || count($file)==0) {
die("File not found");
}
header("Content-Type: " . $file['mime_type']);
readfile($uploaddir.$file['name']);
?>

上述代码文件名随机更改,文件被存储在web root之外,用户通过id在数据库中查询文件名,读取文件,可以有效的阻止上述漏洞发生

总结

通过以上示例分析,可总结一下几点

1.文件名修改,不使用用户上传的文件名

2.用户不可以通过上传路径直接访问文件

3.文件查看采用数据库获取文件名,从而在相应文件服务器读取文件

4.文件上传限制文件大小,个人上传数量等

 以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

PHP 相关文章推荐
聊天室php&amp;mysql(三)
Oct 09 PHP
php dirname(__FILE__) 获取当前文件的绝对路径
Jun 28 PHP
浅析使用Turck-mmcache编译来加速、优化PHP代码
Jun 20 PHP
PHP COOKIE及时生效的方法介绍
Feb 14 PHP
windows下配置apache+php+mysql时出现问题的处理方法
Jun 20 PHP
PHP SPL标准库之接口(Interface)详解
May 11 PHP
PHP递归遍历多维数组实现无限分类的方法
May 06 PHP
PHP上传图片时判断上传文件是否为可用图片的方法
Oct 20 PHP
PHP中的empty、isset、isnull的区别与使用实例
Mar 22 PHP
php+iframe 实现上传文件功能示例
Mar 04 PHP
PHP7 参数处理机制修改
Mar 09 PHP
laravel添加角色和模糊搜索功能的实现代码
Jun 22 PHP
PHP的Json中文处理解决方案
Sep 29 #PHP
PHP二分查找算法示例【递归与非递归方法】
Sep 29 #PHP
PHP快速排序quicksort实例详解
Sep 28 #PHP
PHP实现QQ快速登录的方法
Sep 28 #PHP
PHP自定义错误用法示例
Sep 28 #PHP
PHP构造函数与析构函数用法示例
Sep 28 #PHP
PHP设计模式之工厂模式与单例模式
Sep 28 #PHP
You might like
十天学会php(3)
2006/10/09 PHP
php 无极分类(递归)实现代码
2010/01/05 PHP
解析PHP中的正则表达式以及模式匹配
2013/06/19 PHP
PHP基础知识介绍
2013/09/17 PHP
使用淘宝IP库获取用户ip地理位置
2013/10/27 PHP
PHP实现简单日历类编写
2020/08/28 PHP
jQuery取得select选择的文本与值的示例
2013/12/09 Javascript
json字符串之间的相互转换示例代码
2014/08/21 Javascript
关于网页中的无缝滚动的js代码
2016/06/09 Javascript
滚动条的监听与内容随着滚动条动态加载的实现
2017/02/08 Javascript
详解VUE 定义全局变量的几种实现方式
2017/06/01 Javascript
vuejs父子组件之间数据交互详解
2017/08/09 Javascript
jQuery事件多次绑定与解绑问题实例分析
2019/02/19 jQuery
VUE解决微信签名及SPA微信invalid signature问题(完美处理)
2019/03/29 Javascript
40行代码把Vue3的响应式集成进React做状态管理
2020/05/20 Javascript
element中Steps步骤条和Tabs标签页关联的解决
2020/12/08 Javascript
浅析AST抽象语法树及Python代码实现
2016/06/06 Python
python中实现精确的浮点数运算详解
2017/11/02 Python
Python3多线程爬虫实例讲解代码
2018/01/05 Python
Python实现获取nginx服务器ip及流量统计信息功能示例
2018/05/18 Python
Python定义二叉树及4种遍历方法实例详解
2018/07/05 Python
pandas使用get_dummies进行one-hot编码的方法
2018/07/10 Python
Python的argparse库使用详解
2018/10/09 Python
解决python 未发现数据源名称并且未指定默认驱动程序的问题
2018/12/07 Python
python logging模块书写日志以及日志分割详解
2019/07/22 Python
python 实现return返回多个值
2019/11/19 Python
英国领先的高街书籍专家:Waterstones
2018/02/01 全球购物
linux系统都有哪些运行级别
2012/04/15 面试题
哈弗商学院毕业生求职信
2014/02/26 职场文书
教师节标语大全
2014/10/07 职场文书
党的群众路线教育实践活动实施方案
2014/10/31 职场文书
2014年药房工作总结
2014/11/22 职场文书
小学班主任评语
2014/12/29 职场文书
小学一年级班主任工作经验交流材料
2015/11/02 职场文书
七年级作文之《我和我的祖国》观后感作文
2019/10/18 职场文书
Mysql 如何批量插入数据
2021/04/06 MySQL