Web开发安全之文件上传安全

  非常长壹段时间像笔者那种菜鸡搞一个网址第一时间反应正是找上传,找上传。借此机会把文件上传的安全难点总括一下。

  首先看一下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举行3次渲染,洗掉图片中的恶意代码。

    $img = imagecreatefromjpeg( $uploaded_tmp ); 
    
  6. 动用相对路径回显到前端页面。

     if( rename( $temp_file, ( getcwd() . DIRECTORY_SEPARATOR . $target_path . $target_file ) ) )
    
  • 那多少个年程序员跟自家壹块踩过的雷(应用开发常见的荒唐,对照上文开发流程)
  1. JavaScript前端验证文件类型

    不吹不黑,除了有个别融洽做过的行政和公司站,还是某个一时页面。网络行业还真未有这样写的。简单来说,就是把文件类型通过JavaScript代码验证文件类型。正确通过,错误跳3个alert弹窗。至于怎么绕不多废话了,F1二、burp大法好。小学生错误,不多废话。

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

   
保险安全的文书上传一定要用白名单,同时要证实MIME-TYPE类型。普通的黑名单bypass不过多废话,大家都相比较掌握。印象相比较深的便是某第壹方开发软件,通过黑名单验证的上传文件类型而非白名单。结果jspx这一个文件并未有被黑名单包罗,加之汤姆cat⑥.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打开相关文件能够见到:

    图片 1

    我们再使用getimagesize()函数获取并echo一下互为表里的变量值。

      图片 2

    假设不使用,文件幻数头:

    图片 3

       
 重复上述试验,再次回到false。也便是说在认证了后缀名白名单,MIME-TYPE以及图片幻数后,大家能保险上传的文书一定是叁个图片。可是,还有种遗闻中的东西无法防御。图片马+解析漏洞,或许图片马+蕴涵漏洞。

   4. 图片二次渲染

   透过GD库的imagecreatefromjpeg()函数,我们得以洗掉文件中的一句话木马,或然恶意代码。保证文件贰进制流中,不带有恶意代码。那对分析漏洞还是隐含漏洞有着不行不易的看守成效。

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

   假设不限制上传覆盖.htaccess文件,我们上述的有着努力都只怕白费。

  • 总结:

   本篇仅仅从代码设计层面去怀想文件上传的安全性,未涉嫌有关的运营安全题材。例如Nginx与Apache的分析漏洞也应有在戍守思考在那之中。以及PHP所发生的00截断难题。那里不详加赘述。文章如有错误,欢迎我们指正。

   

 

    

 

 

    

相关文章