你不知道的文件上传漏洞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(1)
Oct 09 PHP
php并发对MYSQL造成压力的解决方法
Feb 21 PHP
解析PHP将对象转换成数组的方法(兼容多维数组类型)
Jun 21 PHP
php 下载保存文件保存到本地的两种实现方法
Aug 12 PHP
php将access数据库转换到mysql数据库的方法
Dec 24 PHP
使用PHP生成图片的缩略图的方法
Aug 18 PHP
Smarty分页实现方法完整实例
May 11 PHP
Smarty保留变量用法分析
May 23 PHP
php基于PDO连接MSSQL示例DEMO
Jul 13 PHP
PHP基于反射机制实现插件的可插拔设计详解
Nov 10 PHP
Laravel学习教程之model validation的使用示例
Oct 23 PHP
PHP实现提高SESSION响应速度的几种方法详解
Aug 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
颠覆常识!无色透明的咖啡诞生了(中日双语)
2021/03/03 咖啡文化
作为PHP程序员应该了解MongoDB的五件事
2013/06/03 PHP
JavaScript入门教程(3) js面向对象
2009/01/31 Javascript
JavaScript 滚轮事件使用说明
2010/03/07 Javascript
jQuery.query.js 取参数的两点问题分析
2012/08/06 Javascript
javascript数组的使用
2013/03/28 Javascript
js将控件隐藏的方法及display属性介绍
2013/07/04 Javascript
JavaScript中的Math.atan2()方法使用详解
2015/06/15 Javascript
jQuery获取radio选中项的值实例
2016/06/18 Javascript
jQuery ajax MD5实现用户注册即时验证功能
2016/10/11 Javascript
用AngularJS来实现监察表单按钮的禁用效果
2016/11/02 Javascript
Jquery Easyui日历组件Calender使用详解(23)
2016/12/18 Javascript
前端开发之CSS原理详解
2017/03/11 Javascript
浅谈node模块与npm包管理工具
2018/01/03 Javascript
详解vue移动端项目代码拆分记录
2019/03/15 Javascript
[08:29]DOTA2每周TOP10 精彩击杀集锦vol.7
2014/06/25 DOTA
[42:25]2018DOTA2亚洲邀请赛 4.5 淘汰赛 LGD vs Liquid 第三场
2018/04/06 DOTA
Python使用urllib模块的urlopen超时问题解决方法
2014/11/08 Python
Python OpenCV处理图像之图像像素点操作
2018/07/10 Python
python实现决策树分类(2)
2018/08/30 Python
python的schedule定时任务模块二次封装方法
2019/02/19 Python
Python绘图实现显示中文
2019/12/04 Python
python opencv如何实现图片绘制
2020/01/19 Python
Python读取xlsx数据生成图标代码实例
2020/08/12 Python
css3 图片圆形显示 如何CSS将正方形图片显示为圆形图片布局
2014/10/10 HTML / CSS
美国内衣第一品牌:Hanes(恒适)
2016/07/29 全球购物
Foot Locker意大利官网:全球领先的运动鞋和服装零售商
2017/05/30 全球购物
说说在weblogic中开发消息Bean时的persistent与non-persisten的差别
2013/04/07 面试题
酒店总经理工作职责
2013/12/13 职场文书
经济类毕业生求职信
2014/06/26 职场文书
竞选大队干部演讲稿
2014/09/11 职场文书
德能勤绩廉个人总结
2015/02/14 职场文书
2015年信访维稳工作总结
2015/04/07 职场文书
学生党支部工作总结2015
2015/05/26 职场文书
观后感开头
2015/06/19 职场文书
python geopandas读取、创建shapefile文件的方法
2021/06/29 Python