在响应中,仅使用Content-Type: text/plain可以防止恶意JavaScript执行吗?

9
我们有一个返回信息的Web应用程序。
HTTP/1.1 400 Bad Request
...
Content-Type: text/plain;charset=UTF-8
Content-Length: 57
Date: Tue, 14 Apr 2015 19:24:54 GMT
Connection: close

Invalid project area item id 

<script>alert(1086)</script>

据我了解,仅依靠“Content-Type: text/plain;charset=UTF-8”来防止JavaScript执行是不够的。相反,输出应该被编码,输入可能需要进行验证并且垃圾内容应该被排除。
我正在寻找的是一些清晰明确、官方认可的答案,关于处理响应中包含JavaScript而Content-Type已设置为text/plain的正确方法。
有人能提供一个链接(或答案)以官方示例的形式展示如何处理这种情况吗?或者说,“Content-Type: text/plain;charset=UTF-8”就足够了?

1
这个错误信息是由什么导致的?你输出的内容或者方式并不重要,关键在于它在另一端怎么使用。 - Marc B
3个回答

10

这里有两种情况。

顶级XSS攻击

如果攻击者可以操纵顶级URL以响应类似于以下的格式不正确的请求:

HTTP/1.1 400 Bad Request
...
<script>alert(document.cookie)</script>

然后设置Content-Type: text/plain有助于缓解XSS攻击(请参见下面的详细答案)。

AJAX XSS

然而,如果攻击者可以操纵目标网页中的某些AJAX函数,并诱使其执行类似于$("#result").html(xss_request_result)的操作,则会有效地将文本加载到Web上下文中,并由浏览器(包括JS)进行解析,此时所有的赌注都是打开的

智慧的做法是实体编码去除标签这样的错误消息响应。


关于第一个场景,根据w3.org的说法:

文本的主要子类型是“plain”。这表示纯文本(未格式化的文本)。互联网邮件的默认Content-Type,“text/plain; charset=us-ascii”,描述了现有的互联网实践,即它是RFC 822定义的正文类型。

这意味着text/plain不应被解释和处理。然而,Google(更新于2011年3月30日)指出:

如果Content-Type匹配通用值之一,例如application/octet-stream、application/unknown或甚至text/plain,许多浏览器会将其视为根据上述信号猜测该值的许可,并试图提供更具体的值来解释它。这一步的理由是,一些配置不良的Web服务器在所有返回的内容上都退回到这些类型。

根据标题为HTML是否被嗅探在文本/纯文本文档中(具有或不具有URL中的文件扩展名)?的浏览器嗅探调查结果如下:

  • Internet Explorer 9+:否
  • FireFox:否
  • Opera:否
  • Chrome:否
  • Android:否
  • Safari:

因此,将内容类型设置为text/plain并显式设置字符集将有助于缓解XSS攻击,但从上述调查结果可以看出,Safari可能会被排除在外。

测试您的浏览器是否存在此漏洞。转到一个更慷慨的在线PHP fiddle网站(例如http://phptester.net/),并执行以下操作。您不应该收到弹出窗口。

<?php
header("Content-Type: text/plain");
echo "<script>alert(1)</script>";

关于内容嗅探型 XSS 攻击的更多阅读材料(PDF)



2
Safari似乎不再在text/plain上嗅探HTML。 - Allison

3

2

德雷克上面的答案让我感到困扰,所以我创建了一个简单的概念证明来检查我是否正确。我是正确的。即使使用Content-Type: text/plain;charset=UTF-8,应用程序也可以被简单的XSS攻击破坏。

原因就像我第一次尝试解释的那样,重要的是数据处理,以及数据的最终目的地和渲染上下文。传输并不是那么重要。我创建了一个简单的servlet,返回与OP相同的响应,包括Content-Type头。以下是该响应:

HTTP/1.1 400 Bad Request
Server: Apache-Coyote/1.1
Cache-Control: no-cache
Content-Type: text/plain;charset=UTF-8
Content-Length: 73
Date: Thu, 18 Jun 2015 22:49:01 GMT
Connection: close


Invalid project area item id <iframe src=javascript:alert(1)></iframe> 

这里是结果的图片。注意,攻击载荷已被执行:https://flic.kr/p/uRnSgo

再次强调,原因非常简单。数据不是在 AJAX 请求中呈现,而是在消费者 Web 应用程序页面中呈现,该页面一个 HTML 页面。

无论如何,我希望这能消除任何关于在某些情况下容易受到攻击的疑虑......尤其是当响应针对将在消费页面中呈现的 AJAX 请求时。

----- 下面是我的原始回复。 -----

带有错误消息的 400 响应让我想起了 REST API 响应。

如果这是一个 REST 请求(请求头中包含 X-Requested-With: XMLHttpRequestAccept: application/json),那么你会面临一个严重问题。尽管此响应未受到影响,但最终用户的 UI 可能会拾取并显示数据。由于没有正确编码,它被执行。您不仅要担心此响应,还要考虑攻击载荷的最终处理方式。假设这是 XMLHttpRequest 或 REST 调用的响应,则这是个严重的漏洞。

您可以使用攻击载荷 <iframe src=javascript:alert(1)></iframe> 进行测试,我敢打赌您会在消费应用程序中看到它。

我建议:使用无效的项目区域项 ID,并省略无效值。这是最便宜的解决方案。

因此,在一般情况下,您不能仅仅依靠Content-Type来保护自己。数据可能会在另一个上下文中呈现,并且被执行。

始终验证输入并正确处理输出,其中可能包括根据将呈现该输出的上下文对其进行编码。任何告诉您否则的人都试图摆脱某些必要工作。:-)


如果你将任何网络响应导入到网络上下文中,那么它当然会在网络上下文中被解析。这是网络基础知识。如果你直接伪造一个无效的请求并得到JS作为结果,并且上下文是text/plain,那么我的答案依然成立。 - Drakes
OP的问题:Content-Type: text/plain;charset=UTF-8是不是就足够了? 回答: 不,那还不够。你需要考虑数据将被用在哪里,并进行适当的编码。Drakes,你上面的新回答已经改进了,谢谢。原来的回答说没有什么可担心的是误导性的。 - Frugal Guy

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接