自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

針對XSS漏洞的前端防火墻:內聯(lián)事件攔截

安全 應用安全
關于 XSS 漏洞怎樣形成、如何注入、能做什么、如何防范,前人已有無數(shù)的探討,這里就不再累述了。本文介紹的則是另一種預防思路。

關于XSS漏洞怎樣形成、如何注入、能做什么、如何防范,前人已有無數(shù)的探討,這里就不再累述了。本文介紹的則是另一種預防思路。

幾乎每篇談論 XSS 的文章,結尾多少都會提到如何防止,然而大多萬變不離其宗。要轉義什么,要過濾什么,不要忘了什么之類的。盡管都是眾所周知的道理,但 XSS 漏洞十幾年來幾乎從未中斷過,不乏一些大網站也時常爆出,小網站更是家常便飯。

預警系統(tǒng)

事實上,至今仍未有一勞永逸的解決方案,要避免它依舊使用最古老的土辦法,逐個的過濾。然而人總有疏忽的時候,每當產品迭代更新時,難免會遺漏一些新字段,導致漏洞被引入。

即使圣人千慮也有一失,程序出 BUG 完全可以理解,及時修復就行。但令人費解的是,問題出現(xiàn)到被發(fā)現(xiàn),卻要經過相當長的時間。例如不久前貼吧 XSS 蠕蟲腳本,直到大規(guī)模爆發(fā)后經用戶舉報,最終才得知。其他網站大多也類似,直到白帽子們挖掘出漏洞,提交到安全平臺上,最終廠商才被告知。若遇到黑客私下留著這些漏洞慢慢利用,那只能聽天由命了。

因此,要是能有一套實時的預警系統(tǒng),那就更好了。即使無法阻止漏洞的發(fā)生,但能在漏洞觸發(fā)的第一時間里,通知開發(fā)人員,即可在最短的時間里修復,將損失降到最低。各式各樣的應用層防火墻,也由此產生。

不過,和傳統(tǒng)的系統(tǒng)漏洞不同,XSS 最終是在用戶頁面中觸發(fā)的。因此,我們不妨嘗試使用前端的思路,進行在線防御。

DOM 儲存型 XSS

先來假設一個有 BUG 的后臺,沒有很好處理用戶輸入的數(shù)據(jù),導致 XSS 能被注入到頁面:

  1. <img src="{路徑}" /> 
  2. <img src="{路徑" onload="alert(/xss/)}" /> 

只轉義尖括號,卻忘了引號,是 XSS 里最為常見的。攻擊者們可以提前關閉屬性,并添加一個極易觸發(fā)的內聯(lián)事件,跨站腳本就這樣被輕易執(zhí)行了。

那么,我們能否使用前端腳本來捕獲,甚至攔截呢?

被動掃描

最簡單的辦法,就是把頁面里所有元素都掃描一遍,檢測那些 on 開頭的內聯(lián)屬性,看看是不是存在異常:

例如字符數(shù)非常多,正常情況下這是很少出現(xiàn)的,但XSS 為了躲避轉義有時會編碼的很長;例如出現(xiàn)一些 XSS 經常使用的關鍵字,但在實際產品里幾乎不會用到的。這些都可以作為漏洞出現(xiàn)的征兆,通知給開發(fā)人員。

不過,土辦法終究存在很大的局限性。在如今清一色的 AJAX 時代,頁面元素從來都不是固定的。伴隨著用戶各種交互,新內容隨時都可能動態(tài)添加進來。即使換成定期掃描一次,XSS 也可能在定時器的間隔中觸發(fā),并銷毀自己,那樣永遠都無法跟蹤到了。況且,頻繁的掃描對性能影響也是巨大的。

如同早期的安全軟件一樣,每隔幾秒掃描一次注冊表啟動項,不僅費性能,而且對惡意軟件幾乎不起作用;但之后的主動防御系統(tǒng)就不同了,只有在真正調用 API 時才進行分析,不通過則直接攔截,完全避免了定時器的間隔遺漏。

因此,我們需要這種類似的延時策略 —— 僅在 XSS 即將觸發(fā)時對其分析,對不符合策略的元素,進行攔截或者放行,同時發(fā)送報警到后臺日志。

主動防御

『主動防御』,這概念放在前端腳本里似乎有些玄乎。但不難發(fā)現(xiàn),這僅僅是執(zhí)行優(yōu)先級的事而已 —— 只要防御程序能運行在其他程序之前,我們就有了可進可退的主動權。對于無比強大的 HTML5 和靈活多變的 JavaScript,這些概念都可以被玩轉出來。

繼續(xù)回到剛才討論的內聯(lián)事件 XSS 上來。瀏覽器雖然沒提供可操控內聯(lián)事件的接口,但內聯(lián)事件的本質仍是一個事件,無論怎樣變化都離不開 DOM 事件模型。

扯到模型上面,一切即將迎刃而解。模型是解決問題的最靠譜的辦法,尤其是像 DOM-3-Event 這種早已制定的模型,其穩(wěn)定性毋庸置疑。

即便沒仔細閱讀官方文檔,但凡做過網頁的都知道,有個 addEventListener 的接口,并取代了曾經一個古老的叫 attachEvent 的東西。盡管只是新增了一個參數(shù)而已,但正是這個差別成了人們津津樂道的話題。每當面試談到事件時,總少不了考察下這個新參數(shù)的用途。盡管在日常開發(fā)中很少用到它。

針對XSS漏洞的前端防火墻:內聯(lián)事件攔截

關于事件捕獲和冒泡的細節(jié),就不多討論了。下面的這段代碼,或許能激發(fā)你對『主動防御』的遐想。

  1. <button onclick="console.log('target')">CLICK ME</button> 
  2. <script> 
  3.     document.addEventListener('click', function(e) {  
  4.         console.log('bubble');  
  5.     });  
  6.  
  7.     document.addEventListener('click', function(e) {  
  8.         console.log('capture');  
  9.         //e.stopImmediatePropagation();  
  10.     }, true);  
  11. </script> 

Run

盡管按鈕上直接綁了一個內聯(lián)的事件,但事件模型并不買賬,仍然得按標準的流程走一遍。capture,target,bubble,模型就是那樣固執(zhí)。

不過,把那行注釋的代碼恢復,結果就只剩 capture 了。這個簡單的道理大家都明白,也沒什么好解釋的。

但仔細揣摩下,這不就是『主動防御』的概念嗎?捕獲程序運行在內聯(lián)事件觸發(fā)之前,并且完全有能力攔截之后的調用。

上面的 Demo 只是不假思索攔截了所有的事件。如果我們再加一些策略判斷,或許就更明朗了:

 

  1. <button onclick="console.log('xss')">CLICK ME</button> 
  2. <script> 
  3.     document.addEventListener('click', function(e) {  
  4.         console.log('bubble');  
  5.     });  
  6.  
  7.     document.addEventListener('click', function(e) {  
  8.         var eelement = e.target;  
  9.         var code = element.getAttribute('onclick');  
  10.  
  11.         if (/xss/.test(code)) {  
  12.             e.stopImmediatePropagation();  
  13.             console.log('攔截可疑事件:', code);  
  14.         }  
  15.     }, true);  
  16. </script> 

 

Run

我們先在捕獲階段掃描內聯(lián)事件字符,若是出現(xiàn)了『xss』這個關鍵字,后續(xù)的事件就被攔截了;換成其他字符,仍然繼續(xù)執(zhí)行。同理,我們還可以判斷字符長度是否過多,以及更詳細的黑白名單正則。

怎么樣,一個主動防御的原型誕生了吧。

不過,上面的片段還有個小問題,就是把事件的冒泡過程也給屏蔽了,而我們僅僅想攔截內聯(lián)事件而已。解決辦法也很簡單,把 e.stopImmediatePropagation() 換成 element.onclick = null 就可以了。

當然,目前這只能防護 onclick,而現(xiàn)實中有太多的內聯(lián)事件。鼠標、鍵盤、觸屏、網絡狀態(tài)等等,不同瀏覽器支持的事件也不一樣,甚至還有私有事件,難道都要事先逐一列出并且都捕獲嗎?是的,可以都捕獲,但不必事先都列出來。

因為我們監(jiān)聽的是 document 對象,瀏覽器所有內聯(lián)事件都對應著 document.onxxx 的屬性,因此只需運行時遍歷一下 document 對象,即可獲得所有的事件名。

  1. <img src="*" onerror="console.log('xss')" /> 
  2. <script> 
  3.     function hookEvent(onevent) {  
  4.         document.addEventListener(onevent.substr(2), function(e) {  
  5.             var eelement = e.target;  
  6.             if (element.nodeType != Node.ELEMENT_NODE) {  
  7.                 return;  
  8.             }  
  9.             var code = element.getAttribute(onevent);  
  10.             if (code && /xss/.test(code)) {  
  11.                 element[onevent] = null;  
  12.                 console.log('攔截可疑事件:', code);  
  13.             }  
  14.         }, true);  
  15.     }  
  16.  
  17.     console.time('耗時');  
  18.     for (var k in document) {  
  19.         if (/^on/.test(k)) {  
  20.             //console.log('監(jiān)控:', k);  
  21.             hookEvent(k);  
  22.         }  
  23.     }  
  24.     console.timeEnd('耗時');  
  25. </script> 

Run

現(xiàn)在,無論頁面中哪個元素觸發(fā)哪個內聯(lián)事件,都能預先被我們捕獲,并根據(jù)策略可進可退了。

性能優(yōu)化

或許有些事件沒有必要捕獲,例如視頻播放、音量調節(jié)等,但就算全都捕捉也耗不了多少時間,基本都在 1ms 左右。

當然,注冊事件本來就花不了多少時間,真正的耗費都算在回調上了。盡管大多數(shù)事件觸發(fā)都不頻繁,額外的掃描可以忽律不計。但和鼠標移動相關的事件那就不容忽視了,因此得考慮性能優(yōu)化。

顯然,內聯(lián)事件代碼在運行過程中幾乎不可能發(fā)生變化。使用內聯(lián)事件大多為了簡單,如果還要在運行時 setAttribute 去改變內聯(lián)代碼,完全就是不可理喻的。因此,我們只需對某個元素的特定事件,掃描一次就可以了。之后根據(jù)標志,即可直接跳過。

  1. <div style="width:100%; height:100%; position:absolute" onmouseover="console.log('xss')"></div> 
  2. <script> 
  3.     function hookEvent(onevent) {  
  4.         document.addEventListener(onevent.substr(2), function(e) {  
  5.             var eelement = e.target;  
  6.  
  7.             // 跳過已掃描的事件  
  8.             var flags = element['_flag'];  
  9.             if (!flags) {  
  10.                 flags = element['_flag'] = {};  
  11.             }  
  12.             if (typeof flags[onevent] != 'undefined') {  
  13.                 return;  
  14.             }  
  15.             flags[onevent] = true;  
  16.  
  17.             if (element.nodeType != Node.ELEMENT_NODE) {  
  18.                 return;  
  19.             }  
  20.             var code = element.getAttribute(onevent);  
  21.             if (code && /xss/.test(code)) {  
  22.                 element[onevent] = null;  
  23.                 console.log('攔截可疑代碼:', code);  
  24.             }  
  25.         }, true);  
  26.     }  
  27.  
  28.     for (var k in document) {  
  29.         if (/^on/.test(k)) {  
  30.             hookEvent(k);  
  31.         }  
  32.     }  
  33. </script> 

Run

這樣,之后的掃描僅僅是判斷一下目標對象中的標記而已。即使瘋狂晃動鼠標,CPU 使用率也都忽略不計了。

到此,在 XSS 內聯(lián)事件這塊,我們已實現(xiàn)主動防御。

對于有著大量字符,或者出現(xiàn)類似 String.fromCharCode,$.getScript 這類典型 XSS 代碼的,完全可以將其攔截;發(fā)現(xiàn)有 alert(/xss/),alert(123) 這些測試代碼,可以暫時放行,并將日志發(fā)送到后臺,確定是否能夠復現(xiàn)。

如果復現(xiàn),說明已有人發(fā)現(xiàn) XSS 并成功注入了,但還沒大規(guī)模開始利用。程序猿們趕緊第一時間修 BUG 吧,讓黑客忙活一陣子后發(fā)現(xiàn)漏洞已經修復了:)

字符策略的缺陷

但是,光靠代碼字符串來判斷,還是會有疏漏的。尤其是黑客們知道有這么個玩意存在,會更加小心了。把代碼轉義用以躲避關鍵字,并將字符存儲在其他地方,以躲過長度檢測,即可完全繞過我們的監(jiān)控了:

  1. <img src="*" onerror="window['ev'+'al'](this.align)" align="alert('a mass of code...')"> 

因此,我們不僅需要分析關鍵字。在回調執(zhí)行時,還需監(jiān)控 eval、setTimeout('…') 等這類能解析代碼的函數(shù)被調用。

不過,通常不會注入太多的代碼,而是直接引入一個外部腳本,既簡單又靠譜,并且能實時修改攻擊內容:

  1. <img src="*" onerror="$['get'+'Script'](...)"> 

下一篇將討論,如何攔截可疑的外部模塊。

原文鏈接:http://fex.baidu.com/blog/2014/06/xss-frontend-firewall-1]

責任編輯:藍雨淚 來源: FEX
相關推薦

2014-06-23 10:58:48

2014-06-24 09:46:08

2014-06-30 14:12:09

XSSXSS漏洞前端防護

2014-06-24 11:46:22

2010-09-14 10:19:39

2016-01-24 23:12:00

UFW防火墻攔截IP地址

2010-09-14 13:08:52

2010-12-21 18:04:26

2015-12-07 16:32:30

2010-12-08 09:29:27

下一代防火墻

2010-09-09 17:22:36

2024-12-30 12:02:29

2011-06-27 13:31:21

2010-05-24 17:49:56

2021-06-25 18:31:37

云防火墻

2021-09-06 11:46:42

Fortinet漏洞防火墻

2019-07-18 11:26:13

防火墻網絡安全軟件

2011-04-18 13:50:43

2011-03-25 11:18:51

2009-09-24 13:53:53

點贊
收藏

51CTO技術棧公眾號