你不知道的文件上传漏洞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 相关文章推荐
防止MySQL注入或HTML表单滥用的PHP程序
Jan 21 PHP
php object转数组示例
Jan 15 PHP
PHP实现ftp上传文件示例
Aug 21 PHP
php查询mssql出现乱码的解决方法
Dec 29 PHP
thinkphp,onethink和thinkox中验证码不显示的解决方法分析
Jun 06 PHP
THINKPHP在添加数据的时候获取主键id的值方法
Apr 03 PHP
PHP+jQuery实现滚屏无刷新动态加载数据功能详解
May 04 PHP
PHP PDOStatement::fetchColumn讲解
Jan 31 PHP
thinkPHP5.1框架中Request类四种调用方式示例
Aug 03 PHP
PHP+Redis事务解决高并发下商品超卖问题(推荐)
Aug 03 PHP
通过PHP实现用户注册后邮箱验证激活
Nov 10 PHP
php7中停止php-fpm服务的方法详解
May 09 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 和 MySQL 基础教程(二)
2006/10/09 PHP
PHP模糊查询的实现方法(推荐)
2016/09/06 PHP
面向对象的编程思想在javascript中的运用上部
2009/11/20 Javascript
原生javascript实现图片轮播效果代码
2010/09/03 Javascript
javascript 按键事件(兼容各浏览器)
2013/12/20 Javascript
js 显示base64编码的二进制流网页图片
2014/04/04 Javascript
JS+CSS3实现超炫的散列画廊特效
2016/07/16 Javascript
js输入框使用正则表达式校验输入内容的实例
2017/02/12 Javascript
Vue监听数据对象变化源码
2017/03/09 Javascript
Angular2开发——组件规划篇
2017/03/28 Javascript
JavaScript瀑布流布局实现代码
2017/05/06 Javascript
layui弹出层效果实现代码
2017/05/19 Javascript
jQuery实现web页面樱花坠落的特效
2017/06/01 jQuery
微信小程序 页面滑动事件的实例详解
2017/10/12 Javascript
ajax请求+vue.js渲染+页面加载的示例
2018/02/11 Javascript
JavaScript中this关键字用法实例分析
2018/08/24 Javascript
在vue中获取微信支付code及code被占用问题的解决方法
2019/04/16 Javascript
vue 实现走马灯效果
2019/10/28 Javascript
JS实现随机抽选获奖者
2019/11/07 Javascript
鸿蒙系统中的 JS 开发框架
2020/09/18 Javascript
python使用PythonMagick将jpg图片转换成ico图片的方法
2015/03/26 Python
使用Python构建Hopfield网络的教程
2015/04/14 Python
numpy.delete删除一列或多列的方法
2018/04/03 Python
Python运行不显示DOS窗口的解决方法
2018/10/22 Python
详解python使用pip安装第三方库(工具包)速度慢、超时、失败的解决方案
2018/12/02 Python
Python编程深度学习绘图库之matplotlib
2018/12/28 Python
解决Python spyder显示不全df列和行的问题
2020/04/20 Python
Django与AJAX实现网页动态数据显示的示例代码
2021/02/24 Python
灵泰克Java笔试题
2016/01/09 面试题
聘用意向书
2014/07/29 职场文书
组织生活会表态发言材料
2014/10/17 职场文书
初中教师个人工作总结
2015/02/10 职场文书
户外拓展训练感想
2015/08/07 职场文书
实习感想范文
2015/08/10 职场文书
团组织关系介绍信
2019/06/24 职场文书
Python&Matlab实现灰狼优化算法的示例代码
2022/03/21 Python