文件上传安全性考虑 | 青训营笔记

467 阅读5分钟

这是我参与「第三届青训营 -后端场」笔记创作活动的第一篇笔记

文件上传漏洞简介

文件上传漏洞是指攻击者上传了一个可执行的脚本文件,并通过此脚本文件获得了执行服务端命令的能力。该漏洞在业务应用系统中出现概率较高,究其原因是业务场景中上传附件、头像等功能非常常见,若在系统设计中忽略了相关的安全检查,则容易导致文件上传漏洞。

业务应用系统中的文件上传功能是导致上传漏洞的重要安全隐患之一。通过文件上传功能,用户可以直接将本地文件上传到服务端,若通过构造URL地址可以直接访问到已上传的文件,则会触发漏洞。例如,若上传的文件是一个非正常服务端文件,如JSP文件、ASP文件、ASPX文件、JSPX文件、PHP文件等可直接执行服务后端代码的文件,则该文件实际可视为“木马文件”。

程序开发中不严格或不安全的逻辑问题会导致文件上传漏洞,程序开发所使用的编程语言以及版本、所用的操作系统,以及不同的应用场景也可能导致文件上传漏洞,所以文件上传漏洞的表现形式与其成因息息相关。

借助文件上传漏洞,攻击者可以获取业务信息系统的WebShell,进一步通过WebShell对该业务系统以及服务器自身的操作系统进行操作,如增加、删除、修改、查看文件等敏感操作。因此相较于其它文件类型的漏洞,文件上传漏洞的危害更大。

产生的原因

一些web应用程序中允许上传图片,文本或者其他资源到指定的位置,文件上传漏洞就是利用这些可以上传的地方将恶意代码植入到服务器中,再通过 URL 去 访问以执行代码。

造成文件上传漏洞的原因是:

•服务器配置不当•开源编辑器上传漏洞•本地文件上传限制被绕过•过滤不严格被绕过•文件解析漏洞导致文件执行•文件路径截断

可以看到之所以会产生文件上传漏洞,主要原因是由于未对上传的文件进行限制以及过滤,因此为了避免漏洞的产生,首先就需要确定上传的文件类型,如何确定文件类型,最简单的是根据文件后缀来判断,例如:

function checkFile() { 
      var file = document.getElementsByName('upload_file')[0].value; 
      if (file == null || file == "") 
         { 
            alert("请选择要上传的文件!");
            return false;
                          } 
          //定义允许上传的文件类型 
          var allow_ext = ".jpg|.png|.gif"; 
          //提取上传文件的类型 
          var ext_name = file.substring(file.lastIndexOf(".")); 
          //判断上传文件类型是否允许上传 
          if (allow_ext.indexOf(ext_name) == -1) 
          { 
            var errMsg = "该文件不允许上传,请上传" + allow_ext + "类型的文件,当前文件类型为:" + ext_name;                     alert(errMsg); 
            return false; 
            } 
            } 

但是文件后缀可以被篡改,如果只有这一种限制方法,非常容易被绕过,需要考虑另外的检测方法,我们采取使用字节流的方式来检测文件的格式,原理如下:

我们都知道,文件的扩展名是用来识别文件类型的。通过给他指定扩展名,我们可以告诉自己,也告诉操作系统我们想用什么方式打开这个文件。比如我么会把.jpg的文件默认用图片显示软件打开,.zip 文件会默认用解压软件打开等等。

然而,扩展名完全是可以随便改改的。我们可以给文件设置一个任意的扩展名,当然也可以不设置扩展名。这样一来我们就不能了解到这个文件究竟是做什么的,究竟是个什么样的文件。我们或许也会疑惑,为什么一个软件,比如视频播放器,就能用正确的方式打开.mp4 .rmvb .wmv 等等的视频?

事实上,所有的文件都是以二进制的形式进行存储的,本质上没有差别。之所以使用的方法不同,只是因为我们理解他的方式不同。在每一个文件(包括图片,视频或其他的非ASCII文件)的开头(十六进制表示)实际上都有一片区域来显示这个文件的实际用法,这就是文件头标志。

Linux下我们可以用file命令直接查看文件的实际格式,但是他本质上也是利用文件头标志来进行文件类型判断的。下面就简要介绍下手动判断文件真实类型的方法。 常见文件格式:

image.png

简单的查看文件头方法如下: windows下 我们可以通过ultraedit之类的文本编辑器检查文件的十六进制表示,很方便。

Linux下 我们有hexdump命令来查看文件的十六进制形式,用这种方法查看png文件的结果大概是这样的:

image.png

当然最重要的是如何使用代码实现检测功能,网上可以查到类似的代码:

image.png

根据自己的代码进行更改即可。

总结

后端校验是防御的核心,主要是禁止对上传的文件目录进行解析,上传的文件随机且检查后缀名,设置文件后缀白名单(在使用PHP的in_array函数进行后缀名检测时,要注意设置此函数的第三个参数为true,不然可通过此函数缺陷绕过检测),对文件内容、大小和类型进行检测等。但目前只是简单的做了一下文件类型的检测,后续会继续学习。