统的Web 页面和应用中,用户每点击页面上的某个部分,浏览器就会向服务器发出一个请求,等待服务器做出响应,然后返回一个完整新网页,但在大多数情况下用户不得不忍受页面闪烁和长时间的等待。随着Web技术的发展和用户体验要求的提高,异步编程是提高用户体验,同时仅仅修改页面中需要修改的部分而大量减少网络流量,增加了实时性。这种异步编程的出现就是以Jesse James Garrett提出的Ajax这一名词得到最广泛的应用。Ajax是异步javascript与XML的缩写,就是对创建异步Web动态应用的一个统称。
一、基础回顾
1、HTTP请求
- 建立连接:IE6及以前版本使用ActiveXObject创建,IE7以后及其他浏览器都支持XMLHttpRequest对象创建。
- 请求类型:传递给XMLHttpRequest对象的open方法的第一个参数来确定,GET或POST。其中GET将数据放在open方法的第二个参数URL后面,只能发送字符串数据,而且大小也有限制。但是POST则是可以发送任意类型数据,大小也无限制(服务器端会规定上限),同时可以使用MIME类型规定发送数据类型,如application/x-www-form-urlencoded、text/xml、application/xml、application/json等。
- <pre code_snippet_id="203158" snippet_file_name="blog_20140225_1_8025697" name="code" class="javascript">function ajax(options){
- if (typeof XMLHttpRequest == "undefined"){
- XMLHttpRequest = function(){
- return new ActiveXObject(navigator.userAgent.indexOf("MSIE 5") >= 0 ? "Microsoft.XMLHTTP" : "Msxml2.XMLHTTP");
- };
- }
- var xml = new XMLHttpRequest();
- options = {
- type: options.type || "GET",
- url: options.url || "",
- timeout: options.timeout || 5000,
- onComplete: options.onComplete || function(){},
- onError: options.onError || function(){},
- onSuccess: options.onSuccess || function(){},
- data: options.data || ""
- };
- xml.open(options.type, options.url, true); //最后一个参数为true是异步请求
- ...
- }</pre>
- <pre></pre>
- 发送数据:发送的数据首先就需要整理格式以便于服务器端易于读取,称为串行化。不管是键值对的json对象还是一组表单值的多个数据,最终就是要串行化为字符串。
- fucntion serialize(a){
- var s =[];
- if (a.constructor == Array){
- for(var i = 0; i < a.length; i++){
- s.push(a[i].name + "=" +encodeURIComponent(a[i].value));
- }
- }else{
- for (var j in a){
- s.push(j + "=" + encodeURIComponent(a[j]));
- }
- }
- return s.join("&");
- }
2、HTTP响应
创建和使用XMLHtttpRequest比其他单向通信的优越之处在于能够从服务器读取不同形式的文本数据,包括其基石之一XML。xhr对象的两个属性包含对应格式的数据:responseXML和responseText。(服务器返回的是XML文档时responseXML存储的就是DOM文档,其他所有响应结果都放在responseText中)。在进行请求后到获取返回数据的这个过程中,有以下两个问题需要关注:处理错误和检查超时。
xhr的整个状态过程如下:
属性 | 描述 |
---|---|
onreadystatechange | 存储函数(或函数名),每当 readyState 属性改变时,就会调用该函数。 |
readyState |
存有 XMLHttpRequest 的状态。从 0 到 4 发生变化。
|
status |
200: "OK" 404: 未找到页面 |
对status的检查以及XMLHttpRequest状态的检查是确保得到正确结果的重要一步。特别是服务器返回的是HTML错误页面而非XML文档的时候。
另一个为了避免由于网络或其他未知原因导致无法及时得到响应时,应该人为设置一个合理的超时时间,超过后自动放弃请求。
3、处理返回数据
通过getResponseHeader函数可以获取返回数据的content-type首部,responseText和responseXML得到相应类型的数据。
- futncion httpData(xhr, type){
- var ct = xhr.getReponseHeader("content-type");
- var data = !type && ct && ct.indexOf("xml") >= 0;
- data = type == "xml" ? xhr.responseXML : xhr.responseText;
- if (type == "script"){
- eval.call(window, data);
- }
- return data;
- }
二、Ajax框架使用
1、jQuery
jQuery封装好了ajax调用,而且有良好的浏览器兼容性。其中$.ajax全局方法是其底层封装,然后以此继续封装了几个高层的方法:
- $.get:封装好的以GET方法发送请求,参数(url,data,callback,datatype)
- $.post:封装好的以POST方法发送请求,参数与get相同
- $.getJSON:以GET方式发送请求,直接返回JSON格式的数据,不用传递返回数据类型的参数
- $.getScript:以GET方式发送请求,直接返回Script格式的文本,不用传递返回数据类型的参数
2、Dojo
- dojo.require("dojo.DeferredList");
- dojo.require("dojox.lang.async");
- function getSoftMusic() {
- return dojo.xhrGet({
- url: "/music/softMusic",
- handleAs: "json"
- });
- }
- function getFolkMusic() {
- return dojo.xhrGet({
- url: "/music/folkMusic",
- handleAs: "json"
- });
- }
- function getCountryMusic() {
- return dojo.xhrGet({
- url: "/music/countryMusic",
- handleAs: "json"
- });
- }
- // 利用 dojo.DeferredList 处理并发的 Ajax 请求
- var d1 = getSoftMusic(), d2 = getFolkMusic(), d3 = getCountryMusic(),
- defList = new dojo.DeferredList([d1, d2, d3]);
- defList.then(function(results) {
- dojo.forEach(results, function(result) {
- console.log(result[1]);
- });
- });
- // 利用 dojox.lang.async.par 处理并发的 Ajax 请求
- var arrayOfAjax = [getSoftMusic, getFolkMusic, getCountryMusic];
- dojox.lang.async.par(arrayOfAjax)().then(function(results) {
- dojo.forEach(results, function(result) {
- console.log(result);
- });
- });
串行ajax:
- dojo.require("dojox.lang.async");
- function deleteSoftMusic() {
- return dojo.xhrDelete({
- url: "/music/softMusic",
- handle: function(response) {
- console.log(response);
- }
- });
- }
- function deleteFolkMusic() {
- return dojo.xhrDelete({
- url: "/music/folkMusic",
- handle: function(response) {
- console.log(response);
- }
- });
- }
- function deleteCountryMusic() {
- return dojo.xhrDelete({
- url: "/music/countryMusic",
- handle: function(response) {
- console.log(response);
- }
- });
- }
- var arrayOfAjax = [deleteSoftMusic, deleteFolkMusic, getCountryMusic];
- dojox.lang.async.seq(arrayOfAjax)().allBoth(function(result) {
- console.log(result);
- });
三、关注漏洞
Ajax 吸引 Web 开发人员的地方也是对其安全性最易受威胁的地方。众多安全专家一致认为 Web 应用程序是网络犯罪的一大目标,但是 Web 安全通常只是企业安全预算的一小部分。由于 Ajax 被用于开发很多可在 Web 上看到的应用程序,因此它的流行使其成为攻击者在 JavaScript 代码中寻找漏洞的目标。
1、基于浏览器的攻击
JavaScript 是 Ajax 的基础,恶意代码经常使用它。要让一个基于浏览器的攻击生效,恶意代码必须能够利用 Web 技术(在此是 JavaScript),使浏览器自己运行攻击者希望运行的代码。一个简单的例子是,在发生一个基于浏览器的攻击时,受害者会发现其主页被篡改,或者当受害者在其浏览器地址栏中输入一个 URL 时,会被重定向到另一个网站。尽管这令人讨厌且很麻烦,但这些都不是最坏的情形。
许多基于浏览器的攻击被设计用来阻止受感染电脑发送通知或减少其他攻击。通常,对受害者浏览器进行的攻击会阻止他们访问恶意软件清除程序或使用 Web 订阅文件更新。其他威胁还包括浏览器代理和击键记录。
预防措施
保护自己不受基于浏览器的攻击比较容易,只需禁用 Java™ 技术、JavaScript 和 Microsoft® ActiveX® 控件即可。不过,这样做通常会阻止运行许多 Web 应用程序。相反,您应该确保 操作系统、浏览器软件和防病毒软件的及时更新。另外,还可以使用一个信誉良好的防火墙程序,并在下载文件和访问网站时小心谨慎。
2、SQL 注入
SQL 注入如何成为了 Ajax 安全的一个威胁?毕竟 Ajax 中没有 “S”。很简单,SQL 注入之所以构成威胁是因为 Ajax 在客户端运行。Web 应用程序服务器端仍然需要 SQL 数据库。
当攻击者在网站开发不完善的区域(比如一个表单)输入恶意代码时,就会发生 SQL 注入。如果受攻击网站比较脆弱,那么该数据库的所有内容都可能会曝光。密码数据库曝光或者从在线支付系统盗取信用卡数据使用的就是这种攻击方法。最近,入侵者利用这种方法从明星网站窃取粉丝邮箱地址。尽管没有盗窃财物,但是垃圾邮件发送者利用了这些邮箱地址,借用明星产品为幌子来传播恶意软件。
与其他 Web 技术一样,应减轻使用 Ajax 开发的应用程序中的 SQL 注入。不过,仅使用基于 JavaScript 的 sanitation 技术尚不足以防止这类利用。JavaScript 是在客户端运行而不是在服务器端运行,这才是发生 SQL 注入的主要原因。
预防措施
在使用 Ajax 时,要保护您的数据库,则必须验证用户输入,而且该验证是在服务器 端进行的。参数化语句或者预处理语句,这些都是为了阻止 SQL 注入,因为不能将值直接输入数据库或者 SQL 语句中。相反,在使用占位符(也称为绑定变量)时,占位符的值是通过一个单独的 API 调用提供的。
3、跨站点脚本
XSS 是注入攻击的另一个示例,恶意代码被注入到应用程序。易受 XSS 攻击的 Web 应用程序包括基于浏览器的脚本,就像那些常见的 Ajax 攻击一样。通常,攻击者利用这类弱点将恶意脚本传递给对该网站毫无戒心的访问者。这些脚本负责盗用身份,窃取 Cookie,暗中监视访客的 Web 使用,访问机密信息,甚至阻止服务攻击。
2010 年成为新闻焦点的著名的 XSS 攻击涉及到社会信息网络。这次攻击是从一个名为 @Matsta 的用户开始的,造成当浏览者鼠标滑过恶意消息时,出现 JavaScript 弹出窗口。其他 XSS 对该网站的攻击导致用户被重定向到一个调查网站或者内容不健康的网站。
预防措施
在使用 Ajax 进行开发时,可以采用以下步骤防止出现 XSS 漏洞
确保javascript变量被引用
使用javascript十六进制编码
使用javascript Unicode编码
使用JSON.parse或json2.js库来分析JSON
避免使用eval方法分析JSON
4、Ajax 桥接
Ajax 应用程序被构建用来连接托管它们的网站。作为一项安全措施,来自站点 A 的应用程序不能连接到站点 B。然而,许多站点依靠第三方网站和数据源来创建混搭网站。人们创建了 Ajax 桥接服务,通过一个主机提供 Web 服务,该服务将充当代理,用来在该浏览器上运行的 JavaScript 和第三方站点之间转发数据。使用 Ajax 桥接,现在站点 A 可以向来自站点 B 的访客提供数据或内容。
正如 Ajax 不是一项特定技术而是一个技术集合,桥接也不是一个特定漏洞。Ajax 桥接为恶意黑客提供了额外攻击途径,增加了威胁。诸如 XSS 和 SQL 注入等攻击可以通过 Ajax 桥接服务传递。尽管站点 B 可能用尽一切办法保护其 Web 应用程序免受访客带来的相应威胁,但是站点 A 可以使用 Ajax 桥接攻击站点 B,这常常被忽视。
预防措施
要避免桥接漏洞,则需要在使用桥接来访问时与第三方的站点之间建立信任。应该审核第三方站点以何种方式访问您的站点,并扫描可能被桥接利用的任何漏洞。