淺談 Dojo 中的安全工具包

來源:互聯網
上載者:User

 安全工作一直是我們日常開發中需要注意的一個問題,對於 Web 開發而言,需要引起我們重視的主要就是 JavaScript 的安全性了。JavaScript 這樣一種指令碼語言可以運行在各種瀏覽器中,但是基於安全性的考慮,幾乎所有的瀏覽器提供給 JavaScript 的介面都是很有限的,尤其是一些安全敏感的介面,如檔案的讀寫操作,記憶體的控制等等。

這麼看似乎 JavaScript 不論怎樣寫都是非常安全的,其實不然。即便是在這樣一個限制重重的環境裡,一樣有很多漏洞可鑽,比如:當您的網站引入了一個外網的 JavaScript,就很容易造成變數的衝突(尤其是全域變數),DOM 操作的混亂等等。如果不對該 JavaScript 作一些限制,那麼我們是很難保證自己的網站不會被一些外界因素所幹擾,甚至崩潰也不是沒有可能。Dojo 的安全工具包提供了一組介面 API 專門用於解決這樣一類安全問題,基於這組安全介面,我們不再需要自己額外加上一些安全方面的控制碼。這篇文章將重點介紹 Dojo 的這套安全工具包的功能和使用方式。

Dojo 的 Secure 工具包簡介

當我們的應用需要和一些外部的指令碼或者資料協同工作時,我們不可避免的要為保證我們自己代碼和資料的安全而加入一些額外校正或檢測代碼。很多情況下,我們不一定能考慮到所有可能的情況,所以我們的應用會存在或多或少的安全隱患。於是,Dojo 的 Secure 工具包應運而生。Dojo 的 Secure 工具包主要包括一些日常經常需要用到的安全相關的一些工具,它封裝了很多安全相關的 API,我們只需要通過簡單的調用即可完成我們安全檢測。

Capability 介面
Dojo 的 Secure 工具包中的 Capability 介面主要用來驗證 JavaScript 指令碼的合法性,以保證在指令碼執行之前確保該指令碼不會訪問或修改它許可權之外的對象或資料。它的使用方式很簡單:

 dojox.secure.capability.validate(script,safeCalls, {document:1,element:1})  var safeCalls = ["isNaN","isFinite","parseInt","parseFloat","escape","unescape", "encodeURI","encodeURIComponent","decodeURI","decodeURIComponent", "alert","confirm","prompt", "Error","EvalError","RangeError","ReferenceError","SyntaxError","TypeError", "Date","RegExp","Number","Object","Array","String","Math", "setTimeout","setInterval","clearTimeout","clearInterval", "dojo","get","set","forEach","load","evaluate" ]; 

這裡主要用到的是“dojox.secure.capability”對象的“validate”方法,第一個參數就是將要執行的指令碼字串,
第二個參數“safeCalls”表示允許的安全函數或對象,它在接下來的代碼中定義了,這裡像“isNaN”,“escape”,“confirm”,“setTimeout”等等都被看作是安全的,而“parent”,“__parent__”,“__proto__ ”,“with”,“caller”這裡是沒有的,它們都有很大的安全隱患,即不推薦使用。如果您的 JavaScript 指令碼裡面調用了“safeCalls”以外的方法會,被檢測為不合法。
第三個參數是定義允許的全域變數,這裡的“document”和“element”是被允許的,其它均為不合法。
所以,在我們引入外部第三方的 JavaScript 指令碼時,可以先驗證一下,確保安全再執行。
當然,它也有一些通用的檢驗規則:

  1. 不推薦方法的聲明,建議使用 var 的方式定義方法。
  2. 多個變數的聲明和賦值不建議用“,”分隔。

這些各種規則都會作為 Dojo 的安全檢測因子,以保證我們引入的外部第三方 JavaScript 指令碼代碼不會對我們的應用造成任何影響。

DOM 相關介面
接下來我們來看看 DOM 相關的安全介面。Dojo 的 Secure 工具包提供了一些對象可以幫我們自動檢測到我們做的 DOM 操作中不合法的一些語句,並拋出相應的異常。基於這些介面和對象,我們可以放心的進行 DOM 結構相關的開發,將檢測的工作扔給 Dojo,我們所要做的僅僅是加入自己的異常處理代碼即可。 

 var div = document.createElement("div");  document.body.appendChild(div);  div.innerHTML = "Sandboxed div:";  div.style.position = "absolute";  div.style.top = "100px";  div.style.left = "100px";  div.style.backgroundColor = "red";  div.style.color = "white";  var container = document.createElement("div");  container.style.backgroundColor = "cyan";  container.style.color = "black";  div.appendChild(container);  wrap = dojox.secure.DOM(container);  securedElement = wrap(container);  console.log("securedElement",securedElement);  securedDoc = securedElement.ownerDocument;  console.log("securedDoc",securedDoc); 

這裡我們簡單地構造了一個 DOM 結構,並將其封裝為“安全”的 DOM 樹:“dojox.secure.DOM(container)”,這個時候,我們可以通過“securedDoc ”對象來進行接下來的操作(注意它的取值:“securedElement.ownerDocument”)。
我們來看幾個合法的樣本:

 securedElement.innerHTML = "Hi there";  t.assertEqual("Hi there",securedElement.data__.innerHTML);  securedDoc.write("<div style='color:red'>written</div>");  securedDoc.close();  t.t(securedElement.data__.innerHTML.match(/written/));  var newDiv = securedDoc.createElement("div");  newDiv.innerHTML = "inner div";  newDiv.style.color="blue";  securedElement.appendChild(newDiv);  t.t(securedElement.data__.innerHTML.match(/inner/));  securedElement.addEventListener("click",function(event) {  alert('proper click handler');  }); 

這裡列出了一些合法的操作,如“innerHTML”,“write”,建立 DIV 以及關聯事件等,操作方式和正常 DOM 操作無異。但是,一旦裡面有一些不安全的操作或者資料,便會有異常拋出,我們看幾個不安全的樣本就瞭解了:

 t.f(securedElement.parentNode); 

很簡單,“parentNode”在安全 DOM 操作中是不允許的,原因就是它可能會破壞上層節點,而其上層節點很可能不在它的允許範圍之內。
還有針對“script”標籤的不安全操作:

try {  securedElement.innerHTML = "<script>bad=true</script>";  }catch(e){}  t.t(typeof bad == 'undefined');  try{  securedElement.innerHTML = '</script><script>bad=true;//';  }catch(e){}  t.t(typeof bad == 'undefined');  try{  securedDoc.write("<script>bad=true;</script>");  }catch(e){}  t.t(typeof bad == 'undefined');  try {  var script = securedDoc.createElement('script');  script.appendChild(securedDoc.createTextNode( 'bad=true'));  securedElement.appendChild(script);  }  catch(e) {}  t.t(typeof bad == 'undefined'); 

指令碼“bad=true”沒有“var”,是不安全的全域變數。
Script 標籤本身寫法就不安全。
通過“write”方法也逃不過安全檢測。
通過構造節點的方式也是行不通的。
針對 CSS,Dojo 的 Secure 包也會做安全檢測:

if (dojo.isIE) {  securedElement.innerHTML =    '<div id="oDiv" style="left:expression((bad=true), 0)">Example DIV</div>';  t.t(typeof bad == 'undefined');  } else {  try{  securedElement.innerHTML = '<input style=\'-moz-binding: url(    "http://www.mozilla.org/xbl/htmlBindings.xml#checkbox");\'>';  }catch(e){}  t.f(securedElement.innerHTML.match(/mozilla/))  }  if (dojo.isIE) {  securedElement.style.left = 'expression(alert("hello"), 0)';  t.f(securedElement.style.left.match(/alert/));  } else {  try {  securedElement.style.MozBinding =     'url("http://www.mozilla.org/xbl/htmlBindings.xml#checkbox")';  }catch(e){}  }  if (dojo.isIE) {  securedElement.style.behavior = 'url(a1.htc)';  t.f(securedElement.style.behavior);  }

Dojo 認為節點的 CSS 樣式中含有如下操作符的都是不安全的:“behavior:|content:|javascript:|binding|expression|@import”。這也很合理,因為這裡的 CSS 操作符對整個頁面都會產生影響。由於清單 6 的例子中含有“expression”,“binding”,“behavior”等,所以,它們都會拋出異常。
接下來,我們來看看它的關於事件綁定的安全檢測:

 securedElement.innerHTML = "<a href='javascript:alert(3)'>illegal link</a>"; 

 try{  securedElement.innerHTML = "<div onclick='alert(4)'>illegal link</div>";  }catch(e){}  t.f(securedElement.innerHTML.match(/alert/)); 

以上的兩種方式都是有安全隱患的,Dojo 會判定其為不安全指令碼並拋出異常。
最後,Dojo 還會攔截不規則的 HTML:

 try {  securedElement.innerHTML = '<div x="\">  <img onload=alert(42)src=http://json.org/img/json160.gif>"></div>';  }catch(e){}  t.f(securedElement.innerHTML.match(/alert/));  try {  securedElement.innerHTML = '<iframe/src="javascript:alert(42)"></iframe>';  }catch(e){}  t.f(securedElement.innerHTML.match(/alert/));  try{  securedElement.innerHTML = '<iframe/ "onload=alert(/XSS/)></iframe>';  }catch(e){}  t.f(securedElement.innerHTML.match(/alert/)); 

這裡的三個樣本顯而易見都是不規範的 HTML,針對這些情況 Dojo 會這屆拋出異常。可能有人覺得通過 HTML 的容錯機制將其“翻譯”成正確的 HTML 即可,但是這種方法往往會有“翻譯”錯誤的情況,並且其中的一些特殊標記可能會造成更大的安全隱患。

JSON 相關
在 JavaScript 中 JSON 資料的處理也是 Web 開發中十分重要的一環,所以 JSON 資料處理的安全也尤為重要。Dojo 的 Secure 工具包提供了安全處理 JSON 資料的介面:

var i, mediumDataSet = [];  for(i = 0; i < 20; i++){  mediumDataSet.push({  prop1: null,  prop2: true,  prop3: false,  prop4: 3.4325222223332266 - i,  prop5: 10003 + i,  prop6: "Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Aenean semper",  prop7: "sagittis velit. Cras in mi. Duis porta mauris ut ligula. Proin porta rutrum",  prop8: "lacus. Etiam consequat scelerisque quam. Nulla facilisi. Maecenas luctus",  prop9: "venenatis nulla. In sit amet dui non mi semper iaculis. Sed molestie",  prop10: " tortor at ipsum. Morbi dictum rutrum magna. Sed vitae risus." + "Aliquam vitae enim." });  }  var mediumJson = dojo.toJson(mediumDataSet);  // 安全 JSON 處理 dojox.secure.fromJson(mediumJson);  // 不安全 JSON 處理 dojo.fromJson(mediumJson); 

這裡我們構造了一個 JSON 資料,並給了兩種 JSON 的資料處理方法:“dojox.secure.fromJson”和“dojo.fromJson”,為什麼前者安全?答案很簡單:“dojo.fromJson”是直接調用“eval”來解析 JSON 資料,“dojox.secure.fromJson”是通過字元分析來解析的。大家都知道“eval”是很不安全的,而且它的效率低下。試想一下,如果我們並不知道拿到的資料是否嚴格符合 JSON 的格式,如果它就是一串 JavaScript 指令碼代碼,我們這裡不假思索的調用“eval”將會產生怎樣的後果?所以,這裡強烈建議使用安全的方法:“dojox.secure.fromJson”。

SandBox
最後,我們來看看 SandBox(沙箱),這是一種可以安全運行外部對象的模式,尤其是跨域取得的頁面,JSON,JavaScript 指令碼等等。Dojo 的 Secure 提供了這種介面,類似於“eval”,不同的是它可以保證被執行的代碼不會對您已有的應用造成任何安全問題。

var div = document.createElement("div");  document.body.appendChild(div);  div.innerHTML = "Sandboxed div:";  div.style.position = "absolute";  div.style.top = "100px";  div.style.left = "100px";  div.style.backgroundColor = "red";  div.style.color = "white";  container = document.createElement("div");  container.style.backgroundColor = "cyan";  container.style.color = "black";  div.appendChild(container);  // 安全 dojox.secure.evaluate("element.innerHTML = 'Hi there';",container);  t.assertEqual("Hi there",container.innerHTML);  dojox.secure.evaluate(" document.write(\"<div style='color:red'>written</div>\");",container);  t.t(container.innerHTML.match(/written/));  // 不安全 t.f(dojox.secure.evaluate("document.body",container));  try {  dojox.secure.evaluate("bad = true",container);  }catch(e){}  t.t(typeof bad == 'undefined'); 

這裡我們在頁面上構造了一個 DOM 結構,並分別執行了相應代碼,注意:這裡按照沙箱的模式給予安全限制:您所執行的任何代碼都是“與世隔絕”的,即您是不可能影響到“container”以外的任何節點和指令碼的。基於這些限制因素,我們這裡的“element.innerHTML”和“document.write”都是安全的,而“document.body”(頁面 body 節點)和“bad = true”(全域變數)都是不安全的,因為它們可能會對“container”以外的事物造成影響。
再來看看如何跨域執行代碼,在保證安全的的情況下:

 var sandbox = dojox.secure.sandbox(document.getElementById("sandbox")); 

 // 本地模式 try{  sandbox.evaluate(input);  }catch(e){  alert(e.message || e);  }  // 跨域模式 var input = document.getElementById("jsFile").value;  sandbox.loadJS(input).addErrback(function(result){  alert(result);  });  var input = document.getElementById("htmlFile").value;  sandbox.loadHTML(input).addErrback(function(result){  alert(result);  }); 

如上所列,本地模式的介面很簡單:“sandbox.evaluate”,和之前的基礎介面無異。跨域模式則是基於非同步模式的調用,這裡您只需要通過“addErrback”添加異常處理代碼即可。注意,這裡的“loadJS”和“loadHTML”分別用於執行 JavaScript 和 HTML 程式碼,它們的傳入值應為一段 URL,如:“http://www.sitepen.com/labs/code/secure/dojox/secure/tests/good.js” 或 “http://www.sitepen.com/labs/code/secure/dojox/secure/tests/good.html”。

結束語
這篇文章介紹了 Dojo 中安全工具包的一些特性,從安全的角度闡述了 Dojo 的安全工具包的各種借口,以及我們為什麼需要這些介面。先介紹了 Capability 介面,即如何檢測我們代碼的安全性。進而擴充到 DOM 和 JSON 相關的介面,即哪種操作才是安全的。最後,通過 SandBox 介面介紹了在本地和跨域兩種情況下,如何通過 Dojo 的介面安全的執行相關代碼。本文主要是基於實際的程式碼範例來說明這些介面的用法,簡明直觀,推薦大家在日常開發中多參考。

本文首發於IBM Developerworks:http://www.ibm.com/developerworks/cn/web/1204_zhouxiang_dojosecure/

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.