我们在开发Web应用时,经常会提供文件下载的功能。工程师们一般会考虑遵循“单一原则”,会开发一个将请求中的file
或filePath
作为参数,来下载指定的文件。这样开发一个下载的功能,就能支持所有的下载需求了。
比如,输入这样的URL
就能够下载123.txt
这个文件了。
这样的确很方便,但是,大家有没有想过,这样的功能可能会出现什么样的安全隐患或者漏洞呢?
来,我们先看看例子:
下面是一段提供文件下载的spring mvc
的java代码,使用filePath
来指定要下载的文件。
|
我们做个测试,在下载的目录下添加一个123.txt
的文件。
echo "123abc一二三" > 123.txt |
测试123.txt
是否可以下载。
http http://localhost:8080/file/download.do\?filePath\=123.txt |
看起来上面的代码和测试结果,貌似没有什么问题,也能够方便的提供文件下载服务(只需要将要下载的文件保存在Constants.TMP_PATH
这个常量中指定的目录下就可以了)。
好,貌似下载的工作就完成了,我们可以考虑做别的功能了。
稍等,既然我们这边blog要聊任意文件下载漏洞,那这个漏洞到底是什么呢?我们不是已经指定了文件下载的目录了吗?
那我们在继续做做做测试,在指定的下载目录的上一级来创建一个456.txt
文件。
echo "456def四五六" > ../456.txt |
测试456.txt
是否可以下载。
http://localhost:8080/file/download.do\?filePath\=../456.txt |
啊!!!居然能够下载!!!
其实,还有更恐怖的事情。
# 获取系统用户信息 |
情绪稳定之后,我们肯定要问一问,这个漏洞出现在哪儿呢?
我们来看看这两行代码。
//通过输入的filePath参数+加上预设的下载目录,获取最终下载地址 |
专门把这两行提出来,大家应该能够理解这个漏洞出现在哪儿了吧。
那我们可以通过什么样的方式来解决这个漏洞呢?
- 是否可以通过
HTTP Request
中的Referrer
来做判断? 貌似会误杀。 - 是否可以指定的文件名来做判断? 这样太麻烦了,就不灵活了。
- 是否可以通过操作系统和Web容器的文件读写权限来限制? 研究文件权限,发现根本不可行。
- ……
其实,我们可以通过一个简单粗暴的方式就能解决这个安全漏洞。
if (null == file || !file.exists() |
原理就不解释了,看看Java的File API文档吧。