http://p1.qhimg.com/t010b8eb68450957562.png


前言


如果你正在管理某个网站,而这个网站又允许用户上传某些类型的文件(例如账号头像和附件等等)时,你可以使用类似内容安全策略(CSP)、Suborigins、Referrer-Policy、Content-Dispostion以及X-Content-Type-Options这样的Web浏览器标准(策略)来减少Web应用的受攻击面。网站管理员如果能够限制用户权限,并且有效地管理用户的上传文件,就可以很大程度地降低Web应用受到恶意攻击的可能性。

在本文的示例中,我搭建了一个phpBB论坛,并且论坛中的用户允许直接上传PNG或JPEG格式的头像图片。


设置正确的路径


当用户上传了头像图片之后,这个图片文件将会以类似下方所示的路径进行保存:

1
/download/file.php?avatar=0_5.jpg

那么问题来了,这个存储路径到底是由谁来决定的呢?对于这个问题,file.php文件也许会告诉你答案。但是,如果你之前进行过设置的话,你也可以自定义文件的存储路径。就像现在的情况一样,我们可以在路径中添加/download/目录。

所以,我在我的NginX服务器中进行了如下配置:

1
2
3
4
5
location ~* /download/ {
more_set_headers "Content-Security-Policy form-action 'none'; default-src 'none'; frame-ancestors 'none'";
more_set_headers 'Referrer-Policy no-referrer';
add_header suborigin $rnd;
}

根据内容安全策略的相关规则,我们的Web应用只允许加载来自这个路径的图片文件。如果我们通过路径/download/file.php?avatar=0_5.jpg直接访问这个图片的话,图片可以正常显示,并不会有任何问题。但是,如果这个图片中包含有一个HTML文件的话,它将会被屏蔽(从某种程度上来说)。

在上面给出的示例代码中,我们要将Referrer-Policy设置为no-referrer,因为如果我们不这样做的话,将很有可能泄漏类似登录凭证以及密码口令这样的机密数据,或者是保存在服务器主机中的各种文件。实际上,在我们这个例子中,“_”前面的第一个参数为用户ID(在“?avatar=0_5.jpg”中,用户ID为0),而这个用户ID对于我们的Web应用来说绝对是敏感信息。

其中的$rnd变量是我们生成的一个随机整数,将其赋值给suborigin。我所使用的Perl代码如下所示:

1

perl_set $rnd 'sub { my @chars = ("a","b","c","d","e","f","g","h","i","j","k","l","m","n","o","p","q","r","s","t","u","v","w","x","y","z");

 my $string; $string .= $chars[rand @chars] for 1..40; return $string;}';

将上面的这段代码复制到服务器配置文件nginx.conf的http{}中,然后使用我示例中给出的变量。接下来,为每一个suborigin赋值一个包含四十个字符的随机字符串。

具体如下图所示:

http://p2.qhimg.com/t01ef469888c7df6f26.png

请注意上述代码中黑体加粗的地方。这些header中包含的信息将允许攻击者找出路径所指向的数据内容,包括图片、文本文档、以及zip压缩文件等等。


实践出真知


接下来,让我们开始动手测试一下这些安全保护策略是否可以正常工作。现在,假设有一个黑客可以上传一个HTML文件,而不是之前的PNG或JPG文件了。这个HTML文件包含下列脚本代码:

1
2
3
4
5
6
<script>
console.log("Your HTTP-cookies: " + document.cookie);
console.log("Your Referrer: " + document.referrer);
console.log("This suborigin: " + document.suborigin);
<a href="https://www.whatismyreferer.com/">Click</a>
</script>

测试运行效果


在下面这个视频中,我们对之前部署的安全保护策略进行了测试。正如你所看到的那样,由于我们已经将Suborigin和referrer完全从出站链接中移除了,所以攻击者无法读取任何的cookie数据。需要注意的是,内容安全策略在这里并不能有效地阻止攻击者运行恶意的JavaScript脚本,因此我们可以假设在这种情况下,内容安全策略是可以被绕过的。


演示视频


重新设置了我们的header之后,document.cookie将不会再返回任何cookie数据了。这样一来,即便是在最糟糕的场景下(即攻击者可以向你的服务器上传文件),大多数上传的文件也不会对用户和浏览器造成影响。


总结


1.  使用Origin作为你的全局referrer策略(缓解URL内部源数据发生泄漏);

2.  使用No-referrer来处理静态内容(缓解URL外部源数据发生泄漏);

3.  为指向静态内容的路径部署最严格的内容安全策略(default-src 'none'; form-action 'none'; frame-ancestors 'none');

4.  使用X-Content-Type-Options:nosniff来防止MIME嗅探;

5.  正确使用Content-Disposition header;




本文由 安全客 翻译,作者Mickeyyyyy

原文链接:https://chloe.re/2016/12/04/dealing-with-user-uploaded-files/