日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 前端技术 > HTML >内容正文

HTML

XSS 前端防火墙 —— 天衣无缝的防护

發布時間:2025/1/21 HTML 96 豆豆
生活随笔 收集整理的這篇文章主要介紹了 XSS 前端防火墙 —— 天衣无缝的防护 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

到目前為止,我們防護的深度已經差不多,但廣度還有所欠缺。

例如,我們的屬性鉤子只考慮了 setAttribute,卻忽視還有類似的 setAttributeNode。盡管從來不用這方法,但并不意味人家不能使用。

例如,創建元素通常都是 createElement,事實上 createElementNS 同樣也可以。甚至還可以利用現成的元素 cloneNode,也能達到目的。因此,這些都是邊緣方法都是值得考慮的。

下面我們對之前討論過的監控點,進行逐一審核。

內聯事件執行 eval

在第一篇文章結尾談到,在執行回調的時候,最好能監控 eval,setTimeout('...') 這些能夠解析代碼的函數,以防止執行儲存在其他地方的 XSS 代碼。

先來列舉下這類函數:

  • eval

  • setTimeout(String) / setInterval(String)

  • Function

  • execScript / setImmediate(String)

事實上,利用上一篇的鉤子技術,完全可以把它們都監控起來。但現實并沒有我們想象的那樣簡單。

Function 重寫有意義嗎

Function 是一個全局變量,重寫 window.Function 理論上完全可行吧。

var raw_fn = window.Function;window.Function = function() { alert('調用Function'); return raw_fn.apply(this, arguments); }; var add = Function('a', 'b', 'return a+b'); console.log( add(1, 2) );

重寫確實可行。但現實卻是不堪一擊的:因為所有函數都是 Function 類的實例,所以訪問任何一個函數的 constructor 即可得到原始的 Function。

例如 alert.constructor,就可以繞過我們的鉤子。甚至可以用匿名函數:

(function(){}).constructor

所以,Function 是永遠鉤不住的。

額外的執行方法

就算不用這類函數,仍有相當多的辦法執行字符串,例如:

  • 創建腳本,innerHTML = 代碼

  • 創建腳本,路徑 =?data:代碼

  • 創建框架,路徑 =?javascript:代碼

  • ......

看來,想完全把類似 eval 的行為監控起來,是不現實的。不過作為預警,我們只監控 eval,setTimeout/Interval 也就足夠了。

可疑模塊攔截

第二篇談了站外模塊的攔截。之所以稱之『模塊』而不是『腳本』,并非只有腳本元素才具備執行能力??蚣茼摗⒉寮际强梢赃\行代碼的。

可執行元素

我們列舉下,能執行遠程模塊的元素:

  • 腳本
<script src="..." />
  • 框架
<iframe src="..."> <frame src="...">
  • 插件(Flash)
<embed src="..."> <object data="..."> <object><param name="moive|src" value="..."></object>
  • 插件(其他)
<applet codebase="...">

這些元素的路徑屬性,都應該作為排查的對象。

不過,有這么個元素的存在,可能導致我們的路徑檢測失效,它就是:

<base href="...">

它能重定義頁面的相對路徑,顯然是不容忽視的。

事實上,除了使用元素來執行站外模塊,還可以使用網絡通信,獲得站外的腳本代碼,然后再調用 eval 執行:

AJAX

目前主流瀏覽器都支持跨域請求,只要服務端允許就可以。因此,我們需監控 XMLHttpRequest::open 方法。如果請求的是站外地址,就得做策略匹配。不通過則放棄向上調用,或者拋出一個異常,或者給 XHR 產生一個 400 狀態。

WebSocket

WebSocket 和 XHR 類似,也能通過鉤子的方法進行監控。

不過,值得注意的是,WebSocket 并非是個函數,而是一個類。因此,在返回實例的時候,別忘了將 constructor 改成自己的鉤子,否則就會泄露原始接口

var raw_class = window.WebSocket;window.WebSocket = function WebSocket(url, arg) { alert('WebSocket 請求:' + url); var ins = new raw_class(url, arg); // 切記 ins.constructor = WebSocket; return ins; }; var ws = new WebSocket('ws://127.0.0.1:1000');

另外,因為它是一個類,所以不要忽略了靜態方法或屬性:

  • WebSocket.CONNECTING

  • WebSocket.OPEN

  • ...

因此,還需將它們拷貝到鉤子上。

框架頁消息

HTML5 賦予了框架頁跨域通信的能力。如果沒有為框架元素建立白名單的話,攻擊者可以嵌入自己的框架頁面,然后將 XSS 代碼 postMessage 給主頁面,通過 eval 執行。

不過為了安全考慮,HTML5 在消息事件里保存了來源地址,以識別消息是哪個頁面發出的。

因為是個事件,我們可以使用第一篇文章里提到的方法,對其進行捕獲。每當有消息收到時,可以根據策略,決定是否阻止該事件的傳遞。

// 我們的防御系統 (function() {window.addEventListener('message', function(e) { if (confirm('發現來自[' + e.origin + ']的消息:\n\n' + e.data + '\n\n是否攔截?')) { e.stopImmediatePropagation(); } }, true); })(); window.addEventListener('message', function(e) { alert('收到:' + e.data) }) postMessage('hello', '*');

Run

當然,如果配置了框架頁的白名單,就能完全避免這回事了。所以這項防御可以選擇性的開啟。

事件源

HTML5 新增了一個叫 EventSource 的接口。不過其用法與 WebSocket 非常相似,因此可以使用類似的鉤子進行防御。

到此,我們列舉了各種能執行遠程模塊的方式。事實上,對其防御并不難,難的是收集這些監控點,做到滴水不漏。

API 鉤子

對于動態創建的可執行模塊,我們通過屬性鉤子,來監控其遠程路徑。

創建元素的方法

這一節是針對 Chrome 的,因為它不支持原生訪問器。

  • createElement / createElementNS 無中生有

  • cloneNode 克隆現有

  • innerHTML / outerHTML 工廠創建

前兩種,通過鉤子程序很容易實現。

第三種,因為 inner/outerHTML 是元素的 property,而非 attribute。由于 Chrome 是無法獲取原生訪問器的,所以使用鉤子會導致無法調用上級接口。

再者,inner/outerHTML 傳進來的是字符串。標簽和屬性魚龍混雜,解析字符串肯定是不靠譜的。所以還得先調用原生 innerHTML 批量構建出節點,然后再掃描其中的元素。而這個過程中,節點掛載事件已經觸發了。

所以,無需考慮第三種情況。

你可能會有疑問,既然用節點掛載事件都能搞定,為什么還要前面的鉤子?其實,在第二篇文章?里已經詳細討論了,動態創建的腳本沒法被事件攔截,所以才用鉤子。

而通過 innerHTML 產生的腳本,是不會執行的!這個大家都聽說過吧。

修改屬性的訪問器

通過原型鏈的訪問器鉤子,可以直接監控特定元素的特定 property,完全不影響他人,所以效率非常高。剛才列舉了可以執行遠程模塊的元素,這些元素的路徑屬性,都得進行重寫訪問器。

當然 Chrome 可以忽略這節。

修改屬性的方法

開頭也提到了,除了 setAttribute 外,使用 setAttributeNode 也能設置屬性,甚至還有 setAttributeNS 版本的。

由于 setAttribute 是個經常調用的方法,因此鉤子程序必須做足夠的優化,將額外的檢測消耗降到最低。

新頁面環境

除了使用最簡單的框架,其實還有其他可以獲得新頁面的途徑。

彈窗

通過彈窗也能獲得新頁面環境,大家都知道。但是窗口關閉,也隨之銷毀了,難道還能使用嗎?不妨測試一下:

<style> .aa { color: red }</style> <button id="btn">POPUP</button> <script> btn.onclick = function() { var win = window.open(); var raw_fn = win.Element.prototype.setAttribute; win.close(); setTimeout(function() { console.log(raw_fn); raw_fn.call(btn, 'class', 'aa'); }, 1000); }; </script>

Run

盡管會有瞬間的閃動,但從新窗口里獲取的變量確實被保留下來了,并且依然起作用。因為我們引用著它,所以即使窗口關閉,仍然不會對其內存回收的。

現實中,可以把點擊事件綁在 document 上,這樣用戶隨便點哪里都能觸發,以此獲得純凈的環境。

因此,我們還得把彈窗函數,也通過鉤子保護起來。

除了最常用的 window.open,其實還有:

  • showModalDialog

  • showModelessDialog

opener

如果當前網頁是從其他頁面點擊打開的,無論是彈窗還是超鏈接,window.opener 都記錄著來源頁的環境。

如果是來源頁和自己又是同源站點,甚至還能訪問到來源頁里面的變量。

這種情況相當常見。例如從帖子列表頁,點開一個帖子詳情頁,那么詳情頁是完全可以操控列表頁的。

要解決這個問題也不難,直接給 window.opener 注入防護程序不就可以了,就像對待新出現的框架頁那樣。

但是,window.opener 可能也有自己的 opener,一層層遞歸上去或許有很多。每個頁面也許又有自己的框架頁,因此防護 window.opener 可能會執行非常多的代碼。如果在初始化時就進行,或許會有性能問題。

事實上,這個冷門的屬性幾乎不怎么用到。所以不如做個延時策略:只有第一次訪問 opener 的時候,才對其進行防護。

我們將 window.opener 進行重寫,把它變成一個 getter 訪問器:

var raw_opener = window.opener; var scanned;window.__defineGetter__('opener', function() { if (!scanned) { installHook(raw_opener); scanned = true; } return raw_opener; });

這樣,只要不訪問 opener,就不會觸發對它的防護,做到真正按需執行。

后記

關于防護監控點,也沒有一個完整的答案,能想到多少算多少,以后可以慢慢補充。

但是,裝了那么多的鉤子及事件,對頁面的性能影響有多大呢?

所以,我們還得開發一個測試控制臺,來跟蹤這套系統??纯幢O控全開時,會對頁面產生多大影響。

轉載于:https://www.cnblogs.com/sunshq/p/4028884.html

總結

以上是生活随笔為你收集整理的XSS 前端防火墙 —— 天衣无缝的防护的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。