很长一段时间像我这种菜鸡搞一个网站第一时间反应就是找上传,找上传。借此机会把文件上传的安全问题总结一下。

  首先看一下DVWA给出的Impossible级别的完整代码:

<?php 

if( isset( $_POST[ 'Upload' ] ) ) {
// Check Anti-CSRF token
checkToken( $_REQUEST[ 'user_token' ], $_SESSION[ 'session_token' ], 'index.php' ); // File information
$uploaded_name = $_FILES[ 'uploaded' ][ 'name' ];
$uploaded_ext = substr( $uploaded_name, strrpos( $uploaded_name, '.' ) + 1);
$uploaded_size = $_FILES[ 'uploaded' ][ 'size' ];
$uploaded_type = $_FILES[ 'uploaded' ][ 'type' ];
$uploaded_tmp = $_FILES[ 'uploaded' ][ 'tmp_name' ]; // Where are we going to be writing to?
$target_path = DVWA_WEB_PAGE_TO_ROOT . 'hackable/uploads/';
//$target_file = basename( $uploaded_name, '.' . $uploaded_ext ) . '-';
$target_file = md5( uniqid() . $uploaded_name ) . '.' . $uploaded_ext;
$temp_file = ( ( ini_get( 'upload_tmp_dir' ) == '' ) ? ( sys_get_temp_dir() ) : ( ini_get( 'upload_tmp_dir' ) ) );
$temp_file .= DIRECTORY_SEPARATOR . md5( uniqid() . $uploaded_name ) . '.' . $uploaded_ext; // Is it an image?
if( ( strtolower( $uploaded_ext ) == 'jpg' || strtolower( $uploaded_ext ) == 'jpeg' || strtolower( $uploaded_ext ) == 'png' ) &&
( $uploaded_size < 100000 ) &&
( $uploaded_type == 'image/jpeg' || $uploaded_type == 'image/png' ) &&
getimagesize( $uploaded_tmp ) ) { // Strip any metadata, by re-encoding image (Note, using php-Imagick is recommended over php-GD)
if( $uploaded_type == 'image/jpeg' ) {
$img = imagecreatefromjpeg( $uploaded_tmp );
imagejpeg( $img, $temp_file, 100);
}
else {
$img = imagecreatefrompng( $uploaded_tmp );
imagepng( $img, $temp_file, 9);
}
imagedestroy( $img ); // Can we move the file to the web root from the temp folder?
if( rename( $temp_file, ( getcwd() . DIRECTORY_SEPARATOR . $target_path . $target_file ) ) ) {
// Yes!
echo "<pre><a href='${target_path}${target_file}'>${target_file}</a> succesfully uploaded!</pre>";
}
else {
// No
echo '<pre>Your image was not uploaded.</pre>';
} // Delete any temp files
if( file_exists( $temp_file ) )
unlink( $temp_file );
}
else {
// Invalid file
echo '<pre>Your image was not uploaded. We can only accept JPEG or PNG images.</pre>';
}
} // Generate Anti-CSRF token
generateSessionToken(); ?>

  我们来分析一下文件安全上传的流程:

  1. 取文件最后的扩展名。

    $uploaded_ext  = substr( $uploaded_name, strrpos( $uploaded_name, '.' ) + 1);   
  2. 对上传文件的文件名做随机数重命名操作,DVWA用的是MD5,rand()函数也可以。
     $target_file   =  md5( uniqid() . $uploaded_name ) . '.' . $uploaded_ext;
  3. 采取白名单方式验证文件的后缀名,MIME-TYPE类型,以及文件大小。
        if( ( strtolower( $uploaded_ext ) == 'jpg' || strtolower( $uploaded_ext ) == 'jpeg' || strtolower( $uploaded_ext ) == 'png' ) &&
    ( $uploaded_size < 100000 ) &&
    ( $uploaded_type == 'image/jpeg' || $uploaded_type == 'image/png'
  4. 至关重要的一点,检查是否为真正图片。
    getimagesize( $uploaded_tmp )\\ 若非图片,则返回一条Flase消息。
  5. GD库或image-magick进行二次渲染,洗掉图片中的恶意代码。
    $img = imagecreatefromjpeg( $uploaded_tmp ); 
  6. 采用相对路径回显到前端页面。
     if( rename( $temp_file, ( getcwd() . DIRECTORY_SEPARATOR . $target_path . $target_file ) ) )
  • 那些年程序员跟我一起踩过的雷(应用开发常见的错误,对照上文开发流程)
  1. JavaScript前端验证文件类型

    不吹不黑,除了一些自己做过的政企站,还是一些临时页面。互联网行业还真没有这么写的。简而言之,就是把文件类型通过JavaScript代码验证文件类型。正确通过,错误跳一个alert弹窗。至于怎么绕不多赘述了,F12、burp大法好。小学生错误,不多赘述。

  2. 上传文件黑名单,不验证MIME-TYPE类型。

    保证安全的文件上传一定要用白名单,同时要验证MIME-TYPE类型。普通的黑名单bypass不过多赘述,大家都比较了解。印象比较深的就是某第三方开发软件,通过黑名单验证的上传文件类型而非白名单。结果jspx这个文件没有被黑名单包含,加之Tomcat6.0默认配置文件能正常解析jspx,直接服务器权限就被拿掉了,剩下做的说多了都是泪。

  3. 不验证是否为真正的图片文件。

    仅仅验证后缀名和MIME-TYPE类型是无法判断是否为真正的文件。这时候PHP中主要通过getimagesize()来分辨图片。首先要说一下文件幻数:

      打开winhex我们可以看到,不同图片格式的二进制流是一致的。

      例如GIF文件就是GIF89a,新建了一个.gif文件,通过Notepad++编辑如下:

GIF89a
(...some binary data...)
<?php phpinfo(); ?>
(... skipping the rest of binary data ...)

    我们用winhex打开相关文件可以看出:

    

    我们再使用getimagesize()函数获取并echo一下相关的变量值。

      

    如果不使用,文件幻数头:

    

重复上述实验,返回false。也就是说在验证了后缀名白名单,MIME-TYPE以及图片幻数后,我们能确保上传的文件一定是一个图片。然而,还有种传说中的东西没法防御。图片马+解析漏洞,或者图片马+包含漏洞。

   4. 图片二次渲染

   通过GD库的imagecreatefromjpeg()函数,我们可以洗掉文件中的一句话木马,或者恶意代码。保证文件二进制流中,不包含恶意代码。这对解析漏洞或者包含漏洞有着非常不错的防御作用。

   5. 不限制上传覆盖.htacess文件

   如果不限制上传覆盖.htaccess文件,我们上述的所有努力都可能白费。

  • 总结:

   本篇仅仅从代码设计层面去考虑文件上传的安全性,未涉及相关的运维安全问题。例如Nginx与Apache的解析漏洞也应该在防御考虑当中。以及PHP所产生的00截断问题。这里不详加赘述。文章如有错误,欢迎大家指正。

   

   

 

    

Web开发安全之文件上传安全的更多相关文章

  1. 分享一个FileUtil工具类,基本满足web开发中的文件上传,单个文件下载,多个文件下载的需求

    获取该FileUtil工具类具体演示,公众号内回复fileutil20200501即可. package com.example.demo.util; import javax.servlet.htt ...

  2. 详细阐述Web开发中的图片上传问题

    Web开发中,图片上传是一种极其常见的功能.但是呢,每次做上传,都花费了不少时间. 一个"小功能"花费我这么多时间,真心不愉快. So,要得认真分析下原因. 1.在最初学习Java ...

  3. 百度Web Uploader组件实现文件上传(一)

    Web Uploader WebUploader是由Baidu WebFE(FEX)团队开发的一个简单的以HTML5为主,FLASH为辅的现代文件上传组件.在现代的浏览器里面能充分发挥HTML5的优势 ...

  4. asp.net(c#)开发中的文件上传组件uploadify的使用方法(带进度条)

    上文件传很常见,现在就文件上传利用HTML的File控件(uploadify)的,这里为大家介绍一下(uploadify)的一些使用方法.在目前Web开发中用的比较多的,可能uploadify(参考h ...

  5. iOS多线程与网络开发之小文件上传

    郝萌主倾心贡献,尊重作者的劳动成果,请勿转载. /** 取得本地文件的MIMEType */ 2 - (void) getMIMEType { 3 // Socket 实现断点上传 4 5 //apa ...

  6. [红日安全]Web安全Day5 - 任意文件上传实战攻防

    本文由红日安全成员: MisakiKata 编写,如有不当,还望斧正. 大家好,我们是红日安全-Web安全攻防小组.此项目是关于Web安全的系列文章分享,还包含一个HTB靶场供大家练习,我们给这个项目 ...

  7. ASP.NET Core WEB API 使用element-ui文件上传组件el-upload执行手动文件文件,并在文件上传后清空文件

    前言: 从开始学习Vue到使用element-ui-admin已经有将近快两年的时间了,在之前的开发中使用element-ui上传组件el-upload都是直接使用文件选取后立即选择上传,今天刚好做了 ...

  8. Web应用安全之文件上传漏洞详解

    什么是文件上传漏洞 文件上传漏洞是在用户上传了一个可执行的脚本文件,本通过此脚本文件获得了执行服务器端命令的功能,这种攻击方式是最为直接,最为有效的,有时候,几乎没有什么门槛,也就是任何人都可以进行这 ...

  9. 基于HT for Web矢量实现HTML5文件上传进度条

    在HTML中,在文件上传的过程中,很多情况都是没有任何的提示,这在体验上很不好,用户都不知道到时有没有在上传.上传成功了没有,所以今天给大家介绍的内容是通过HT for Web矢量来实现HTML5文件 ...

随机推荐

  1. mysql每秒最多能插入多少条数据 ? 死磕性能压测

    前段时间搞优化,最后瓶颈发现都在数据库单点上. 问DBA,给我的写入答案是在1W(机械硬盘)左右. 联想起前几天infoQ上一篇文章说他们最好的硬件写入速度在2W后也无法提高(SSD硬盘) 但这东西感 ...

  2. Hello Web API系列教程——Web API与国际化

    软件国际化是在软件设计和文档开发过程中,使得功能和代码设计能处理多种语言和文化习俗,在创建不同语言版本时,不需要重新设计源程序代码的软件工程方法.这在很多成熟的软件开发平台中非常常见.对于.net开发 ...

  3. nodejs进阶(5)—接收请求参数

    1. get请求参数接收 我们简单举一个需要接收参数的例子 如果有个查找功能,查找关键词需要从url里接收,http://localhost:8000/search?keyword=地球.通过前面的进 ...

  4. HTML 事件(三) 事件流与事件委托

    本篇主要介绍HTML DOM中的事件流和事件委托. 其他事件文章 1. HTML 事件(一) 事件的介绍 2. HTML 事件(二) 事件的注册与注销 3. HTML 事件(三) 事件流与事件委托 4 ...

  5. Minor【 PHP框架】1.简介

    1.1 Minor是什么 Minor是一个简单但是优秀的符合PSR4的PHP框架,It just did what a framework should do. 只做一个框架应该做的,简单而又强大! ...

  6. UWP开发之Mvvmlight实践九:基于MVVM的项目架构分享

    在前几章介绍了不少MVVM以及Mvvmlight实例,那实际企业开发中将以那种架构开发比较好?怎样分层开发才能节省成本? 本文特别分享实际企业项目开发中使用过的项目架构,欢迎参照使用!有不好的地方欢迎 ...

  7. Http请求

    HTTP报文是面向文本的,报文中的每一个字段都是一些ASCII码串,各个字段的长度是不确定的.HTTP有两类报文:请求报文和响应报文. 请求报文 一个HTTP请求报文由请求行(request line ...

  8. 【走过巨坑】android studio对于jni调用及运行闪退无法加载库的问题解决方案

    相信很多小伙伴都在android开发中遇到调用jni的各种巨坑,因为我们不得不在很多地方用到第三方库so文件,然而第三方官方通常都只会给出ADT环境下的集成方式,而谷歌亲儿子android studi ...

  9. thinkphp数据的查询和截取

    public function NewsList(){ $this->assign('title','news'); $p = I('page',1); $listRows = 6; $News ...

  10. iOS 后台处理

    iOS 后台处理的常见用途 1.进入后台时候删除资源:应用处于挂起状态的时候所占用的资源越少,该应用被iOS终止的风险就越低.通过从内存中清理那些易于重新创建的资源,可以增加应用驻留内存的机会,因此可 ...