处理用户上传的图像时,防止“内容嗅探”类型的漏洞?

问题描述 投票:4回答:3

The problem:

我开发了一个内部工具,允许用户上传图像 - 然后将这些图像显示给他们和其他人。

这是一个Java / Spring应用程序。我的好处是只需要完全担心IE11和Firefox v38 +(Chrome v43 +会很不错)

在首次开发该功能后,用户似乎可以创建一个文本文件,如:

 <script>alert("malicious code here!")</script>

并将其保存为“maliciousImage.jpg”并上传。

之后,当图像显示在图像标记内时,例如:

 <img src="blah?imgName=foobar" id="someImageID">

actualImage.jpg正常显示,maliciousImage.jpg显示为断开链接 - 最重要的是没有恶意内容被解释!

但是,如果用户右键单击此损坏的链接,并单击“查看图像”......则会发生不好的事情。

浏览器“内容嗅探”一个对我来说很新的概念,检测到'maliciousImage.jpg'实际上是一个文本文件,非常友好地毫不犹豫地将其呈现为HTML。任何脚本标记都会传递给JavaScript解释器,如您所想,我们不希望这样。

What I've tried so far

简而言之,我可以想到每个可能的响应头组合,以防止浏览器内容嗅探。我在stackoverflow和其他文档中找到的所有答案都暗示设置内容类型标头应该可以防止大多数浏览器进行内容嗅探,并且设置X内容选项应该会阻止某些版本的IE。

我将x-content-type-options设置为no sniff,我正在设置响应内容类型。我读过的文档让我相信这应该停止内容嗅探。

response.setHeader("X-Content-Type-Options", "nosniff"); 
response.setContentType("image/jpg");

我正在拦截响应并且这些标头存在,但似乎对恶意内容的处理方式没有影响......

我也尝试在上传时检测哪些图像是恶意的,但我很快意识到这是非常重要的......

End goal:

当然 - 对于非真实图像的图像(乱码,无法处理的异常等)的任何输出都比在明文中以HTML / javascript执行文本文件更好,但是将任何恶意HTML显示为转义/ CDATA纯文本会很理想......虽然可能有点不切实际。

javascript html security xss penetration-testing
3个回答
1
投票

所以我最终解决了这个问题,但忘了回答我自己的问题:

Step 1: blocking invalid images

为了快速解决问题,我简单地添加了一些相当钝的代码,用于检查图像是否实际上是图像 - 在上传期间和服务之前,使用imageio lib:

import javax.imageio.ImageIO;

//...... 

Image img = attBO.getImage(imgId);

InputStream x = new ByteArrayInputStream(img.getData());
BufferedImage s;
try {
    s = ImageIO.read(x);
    s.getWidth();
} catch (Exception e) {
    throw new myCustomException("Invalid image");
}

现在,我最初希望这可以解决我的问题 - 但实际上并不是那么简单,只是让生成有效载荷变得更加困难。

虽然这会阻止:

 <script>alert("malicious code here!")</script>

生成一个也是XSS有效载荷的有效图像是非常可能的 - 只需要多一点努力....

Step 2: framework silliness

事实证明,有一个我从未接触过的整个后处理工作流程,它做了诸如将令牌附加到响应主体并使用其他框架来装饰CSS,页眉,页脚等响应。

这意味着,虽然控制器显式地返回了image / png,但它正被抓取并放置(作为字节)后处理采用该字节流,并将其包装在页眉和页脚中,以形成完全合格的“视图” - 这视图将始终具有'content-type'文本/ html,因此从未正确显示。

这个问题的关键在于我的控制器以RESTful方式直接返回一个图像,当构建框架的其余部分来处理控制器返回完整的视图时。

因此,我不得不逐步完成这个工作流程,并在我的代码中为控制器创建例外,这些控制器返回的东西不是以宁静的方式工作。

例如,使用site-mesh,它只是一个排除(一旦我理解了问题,就像往常一样简单的修复......):

<decorators defaultdir="/WEB-INF/decorators">
<excludes>
    <pattern>*blah.ctl*</pattern>
</excludes>
<decorator name="foo" page="myDecorator.jsp">
    <pattern>*</pattern>
</decorator>

然后是其他一些其他定制的调用后拦截器。

Step 3: Content negotiation

现在,我终于得到了只提供图像字节码的阶段,并且没有指定或明确生成评论。

一个名为“内容协商”的Spring功能被启动。它试图协调请求的“接受”标题,以及它手头上的“messageconverters”来产生这样的响应。

因为spring默认没有messageconverter来生成image / png响应,所以它回落到text / html - 我仍然看到问题。

现在,如果我使用spring 4,我可以简单地添加注释:

@Produces("image/png")

到我的控制器 - 简单修复......

Step 4: Legacy dependencies

但因为我只有3.0.5弹簧(并且无法升级)我不得不尝试其他的东西。

我尝试注册新的消息转换器,但这是一个令人头疼或添加一个新的post-method拦截器,只是简单地将内容类型更改回'image / png' - 但这是一个hacky头痛。

最后,我刚刚在控制器中公开了请求/响应,并将我的图像直接写入响应主体 - 完全绕过了Spring的内容协商

....最后我的图像被用作图像并显示为图像 - 并且没有执行注入的代码!


0
投票

这听起来很奇怪,因为它在其他地方完美运作您确定响应中是否存在X-Content-Type-Options标头?

这是我后来构建的一个演示,我有一个有效的html,gif和javascript文件。正如您所看到的那样,它首先作为HTML加载,但随后将其自身加载为图像和脚本(执行): http://research.insecurelabs.org/content-sniffing/gifjs.html

但是,如果使用“X-Content-Type-Options:nosniff”标头加载它,脚本将不再执行: http://research.insecurelabs.org/content-sniffing/nosniff/gifjs.html

顺便说一句,图像在FF / IE中正确呈现,但在Chrome中则不然。

这是一个演示,我尝试了你所描述的: http://research.insecurelabs.org/content-sniffing/stackexchange.html

第一张图片没有nosniff,第二张图片是,它似乎按预期工作。使用“视图图像”打开时,第二个不会运行脚本。

编辑: Firefox似乎不支持X-Content-Type-Options:nosniff

因此,您还应该添加“Content-disposition:attachment; filename = image.gif”或类似于图像。如果通过图像标记加载,图像将正常加载,但如果直接打开URL,则会强制下载而不是直接在浏览器中显示图像。

示例:http://research.insecurelabs.org/content-sniffing/attachment/


-1
投票

adeneo非常适合。您应该使用您想要检查的任何图像库,以确定上载的文件是否是它声称的类型的有效文件。客户端发送的任何内容都可以被操纵。

© www.soinside.com 2019 - 2024. All rights reserved.