前端安全 — 淺談JavaScript攔截XSS攻擊

天存資訊發表於2021-06-15

XSS/跨站指令碼攻擊,是一種程式碼注入網頁攻擊,攻擊者可以將程式碼植入到其他使用者都能訪問到的頁面(如論壇、留言板、貼吧等)中。

如今,XSS 攻擊所涉及的場景愈發廣泛。越來越多的客戶端軟體支援 html 解析和 JavaScript 解析,比如:HTML 文件、XML 文件、Flash、PDF、QQ、一些音樂播放器以及瀏覽器的功能介面等。這些使用者經常使用的場景往往都是 XSS 攻擊的高發地帶。

一、XSS攻擊型別

1. 儲存型XSS(持久型)

攻擊者在表單內提交惡意 js 程式碼 ( 如 <script>alert('hello')</script> ),網站後端對提交資料不做任何安全處理,直接儲存在資料庫中。當其他使用者訪問這個已被攻擊的網站,js 程式碼攻擊就會被觸發。這個型別的 XSS 攻擊會儲存在資料庫中,持續時間長,影響範圍大。

圖1

2. 反射型XSS(非持久型)

反射型 XSS 攻擊,是正常使用者請求一個非法資源時觸發的攻擊。此型別攻擊通常需要使用者主動去訪問帶攻擊的連結,一旦點開了連結,大概率被成功攻擊(比如:我有一部電影資源,請點選 http://xxxxx 下載)。

圖2

3. DOM XSS

基於 DOM 的 XSS 攻擊是反射型攻擊的變種。伺服器正常返回資料,其攻擊在於正常使用者進行某種操作 ( js 操作) 時,觸發攻擊者的 URL 攻擊程式碼,伺服器難以檢測出這是否為非法請求。

圖3

二、XSS攻擊的危害

  • 掛馬;
  • 盜取使用者cookie;
  • DoS(拒絕服務)客戶端瀏覽器;
  • 釣魚攻擊,高階釣魚技巧;
  • 編寫針對性的 XSS 病毒,刪除目標文章、惡意篡改資料、嫁禍;
  • 劫持使用者 Web 行為, 進一步滲透內網;
  • 爆發 Web2.0 蠕蟲;
  • 蠕蟲式的 DDoS 攻擊;
  • 蠕蟲式掛馬攻擊、刷廣告、刷流量、破壞網上資料
  • ……

三、JavaScript攔截

隨著網際網路發展,XSS 攻擊涉及場景越大,其造成的危害隨之擴大。攔截與防護的關鍵,重中之重當然是從後端入手。然而,這並不意味著網頁前端無需進行相應的攔截。前後端共同攔截,網站應對 XSS 攻擊的防護才會更加周全。接下來,本文將淺析前端的 XSS 攻擊攔截。

XSS 攻擊簡單來說就是程式碼的注入,特指惡意使用者輸入惡意程式程式碼。為了防範這類程式碼的注入,網站需要確保其使用者輸入的安全性。對於攻擊驗證,可以採用以下措施:

  1. 編碼,即轉義使用者的輸入,把使用者的輸入解讀為資料而不是程式碼;
  2. 校驗,對使用者的輸入及請求一律進行黑名單過濾檢查,如對特殊字元進行過濾,設定輸入域的匹配規則等,主要針對三類注入:內聯事件及內聯指令碼靜態指令碼動態指令碼
  3. 建立上報攻擊資訊,對攻擊者攻擊資訊進行分析,增強黑名單。

1. 編碼

在客戶端使用 JavaScript 對使用者輸入進行編碼時,有一些內建的方法和屬性可以在自動感知上下文的情況下,對所有的輸入資料進行編碼。

一些自動編碼的方法可參考下表:

上下文 方法
html元素 例:<div>userinput</div>
node.textContent = userInput
html屬性 例:<input value="userInput"/>
node.setAttribute(attr,userInput)
URL查詢值 例:http://example.com/?parameter=userInput
encodeURIComponent(userInput)
CSS值 例:color: userInput
Node.style.property=userInput

2. 內聯事件及內聯指令碼

一些比較常見的注入方式,大部分都是 javascript:... 及內聯事件 on* 。如:

 <a href="javascript:alert('hello')" ></a>
 <iframe src="javascript:alert('hello')" />
 <img src='x' onerror="alert('hello')" />
 <video src='x' onerror="alert('hello')" ></video>
 <div onclick="alert('hello')" onmouseover="alert('hello2')" ><div>

類似這種注入,我們需要在瀏覽器觸發點選事件前,對 javascript:... 內容進行黑名單判斷,以實現防護效果。對於 on* 也是一樣,可以使用 addEventListener 防護內聯事件注入:

 // 定義 黑名單 策略
 var
 blackList = [
   'xss',
   'flow..'
 ];
 // 黑名單匹配
 function blackMatch(blackList, value) {
   if (!blackList) return false;
   for (var i=0; i <  blackList.length; i++) {
     var reg = new RegExp(blackList[i], 'i');
     if (reg.test(value)) {
       return true;
     } else {
         return false;
     }
 }

 document.addEventListener('click', function(e) {
     var el = e.target;
     //查詢 a標籤 <a href="javascript:">
     if (el.tagName === 'A' && el.protocol === 'javascript:') {
         var code = el.href.substr(11);
         if (blackMatch(blackList, code)) {
             //非法輸入
             elem.href = 'javascript:void(0)';
         }
     }
 }, true)

3. 靜態指令碼

通常,攻擊者會向頁面中注入一個靜態指令碼連結,比如,注入 <scriptsrc="http://xxx/xss.js">。對於這類注入,我們需要在指令碼執行前找出可疑指令碼,並銷燬掉。此處可使用 MutationObserver 高階 api,在頁面載入變化時,對靜態指令碼檔案進行監控:

 // Mutation 監聽DOM樹變化
 var observer = new MutationObserver(function(mutations) {
     mutations.forEach(function(mutation) {
          // 新增節點數
         var nodes = mutation.addedNodes;

         for (var i = 0; i <= nodes.length - 1; i++) {
             var node = nodes[i];

             //blacklist 黑名單策略列表
             if (blackMatch(blackList, code)) {
                 node.parentNode.removeChild(node);
                 console.log('發現可疑模組:', node);
             }
         }
     });
 });

 // 當頁面元素節點新增和刪除操作會被觀察到
 observer.observe(document, {
     subtree: true,
     childList: true
 });

4. 動態指令碼

除上述靜態指令碼外,攻擊者還經常使用動態指令碼注入,而且 MutationObserver api 無法監聽 DOM 的變化,攻擊指令碼依舊會執行。比如:

 var script = document.createElement('script');
 script.setAttribute('type', 'text/javascript');
 script.setAttribute('src', 'http://xxxxx/xss.js');

 document.getElementsByTagName('body')[0].appendChild(script)

這種注入方式,可能需要使用原生的 setAttribute 方法,來監聽 src 屬性的值,再通過黑名單判斷它是否具有合法性,重寫 setAttribute 介面來實現防護:

 var old_setAttribute = Element.prototype.setAttribute;
 
 // 重寫 setAttribute 介面
 Element.prototype.setAttribute = function(name, value) {

   if (this.tagName == 'SCRIPT' && /^src$/i.test(name)) {
     // 黑名單判斷
     if (blackMatch(blackList, value)) {
       console.log('發現可疑模組:', value);
       return;
     }
   }

   old_setAttribute.apply(this, arguments);
 };

5. 上報攻擊資訊

上述幾種前端防護均需要黑名單庫進行判斷,然而如何才能增強黑名單呢?

我們需要將攻擊者每次的攻擊資訊收集起來,併傳送到伺服器進行分析、處理。這樣一來,不僅可以增強黑名單,還能根據收集到的資訊設計針對性措施。

可定義一個上報函式,使用 node 接收攻擊資訊。其結果如下:

 function hijackReport(name, value) {
   var img = document.createElement('img'),
     hijackName = name,
     hijackValue = value.toString(),
     curDate = new Date().getTime();
     
     // 上報
       img.src = 'http://192.168.1.3:3002/report/?msg=' + hijackName + 
       '&value=' + hijackValue + '&time=' + curDate;
 }

圖4

在此,有一個防 XSS 攻擊的庫可供學習瞭解。

Tips - Content Security Policy (CSP)

使用驗證來防止 XSS 攻擊的缺陷在於,只要存在一絲漏洞,就會使網站遭到攻擊,而 Content Security Policy (CSP) 的標準則能夠降低這一風險。

(張傑 | 天存資訊)

Ref

  1. 《Web前端黑客技術揭祕》 - 鍾晨鳴
  2. JavaScript防http劫持與XSS’ - shanyezi
  3. https://github.com/leizongmin/js-xss

相關文章