XSS的中文名稱叫跨站腳本,是WEB漏洞中比較常見的一種,特點就是可以將惡意html/JavaScript代碼注入到受害用戶瀏覽的網頁上,從而達到劫持用戶會話的目的。XSS根據惡意腳本的傳遞方式可以分為3種,分別為反射型、存儲型、DOM型,前面兩種惡意腳本都會經過服務器端然后返回給客戶端,相對DOM型來說比較好檢測與防御,而DOM型不用將惡意腳本傳輸到服務器在返回客戶端,這就是DOM型和反射、存儲型的區別,所以我這里就單獨的談一下DOM型XSS。
DOM文檔
為了更好的理解DOM型XSS,先了解一下DOM,畢竟DOM型XSS就是基於DOM文檔對象模型的。對於瀏覽器來說,DOM文檔就是一份XML文檔,當有了這個標准的技術之后,通過JavaScript就可以輕松的訪問它們了。
下面舉例一個DOM將html代碼轉化成樹狀結構:
<html> <head> <meta charset="gbk" /> <title> TEST </title> </head> <body> <p>The is p.<p> <h1>Product:</h1> <ul> <li>Apple</li> <li>Pear</li> <li>Corn</li> </ul> </body> </html>
轉化成模型如下圖:
這樣做的好處就是,通過這種簡單的樹狀結構,就能把元素之間的關系簡單明晰的表示出來,方便客戶端的JavaScript腳本通過DOM動態的檢查和修改頁面內容,不依賴服務端的數據。
利用原理
客戶端JavaScript可以訪問瀏覽器的DOM文本對象模型是利用的前提,當確認客戶端代碼中有DOM型XSS漏洞時,並且能誘使(釣魚)一名用戶訪問自己構造的URL,就說明可以在受害者的客戶端注入惡意腳本。利用步驟和反射型很類似,但是唯一的區別就是,構造的URL參數不用發送到服務器端,可以達到繞過WAF、躲避服務端的檢測效果。
為了更方便大家的理解,下面我舉幾個場景給大家理解。
場景一:innerHTML
<html> <head> <title> DOM-XSS TEST </title> <style> #box{width:250px;height:200px;border:1px solid #e5e5e5;background:#f1f1f1;} </style> </head> <body> <script> window.onload= function(){ var oBox=document.getElementById("box"); var oSpan=document.getElementById("span1"); var oText=document.getElementById("text1"); var oBtn=document.getElementById("Btn"); oBtn.onclick = function(){ oBox.innerHTML = oBox.innerHTML + oSpan.innerHTML + oText.value + "<br/>"; // oBox.innerHTML += oSpan.innerHTML + oText.value + "<br/>";//這是簡便的寫法,在js中 a=a+b ,那么也等同於 a+=b oText.value="" }; } </script> <div id="box"></div> <span id="span1">小明:</span> <input type="text" id="text1"/> <input id="Btn" type="button" value="發送消息" name=""/> </body> </html>
第一次是正常訪問:hellow
第二次是將JavaScript代碼作為參數寫入值中:<svg/onload=alert(1)>
這里我只在火狐瀏覽器利用成功,在chrome利用失敗,我猜可能chrome對安全防護做得比較好,這里不繼續各個瀏覽器版本問題。
使用innerHTML、outerHTML 時要注意,標簽需不進行編碼處理,可能會導致XSS。防護方法就是替換成innerText,它自動將HTML標簽解析為普通文本,所以HTML標簽不會被執行,避免XSS攻擊。
oBox.innerText = oBox.innerHTML + oSpan.innerHTML + oText.value + "<br/>";
場景二:跳轉
<html> <head> <title> DOM-XSS TEST </title> </head> <body> <script> var hash = location.hash; if(hash){ var url = hash.substring(1); location.href = url; } </script> </body> </html>
正常訪問是用#去實現頁面跳轉,但是因為跳轉部分參數可控,可能導致Dom xss。
通過 location.hash 的方式,將參數寫在 # 號后,既能讓js讀取到該參數,又不讓該參數傳入到服務器,從而避免了WAF的檢測。
變量hash作為可控部分,並帶入url中,變量hash控制的是#之后的部分,可以使用偽協議#javascript:alert(1)。常見的幾種偽協議有javascript:、vbscript:、data:等。而現在的移動端(android和ios),都可以自定義這種協議從瀏覽器打開本地app,具體可以看看https://www.cnblogs.com/WuXiaolong/p/8735226.html
#javascript:alert(1)
場景三:eval
#';alert(1);//
直接將用戶輸入數據拼接到代碼里。
eval("var x = '" + location.hash + "'");
場景四:cookie、referrer
從localStorage、SessionStorage、Cookies儲存源中取數據,這些值往往會被開發者忽略,認為這些值都是在瀏覽器獲取的,是安全的,就未進行處理。
var cookies = document.cookie; document.write(cookies);
場景五:document.write 、document.URL.indexOf("id=")
var ids = document.URL; document.writeln(ids.substring(ids.indexOf("id=")+3,ids.length));
indexOf獲取url里面的參數,然后通過writeln( )或者write( )輸出到HTML,造成xss,不過我現在(2020.3)在chrome和firefox瀏覽器測試,write()函數很難利用,除非結合一些特殊場景。
廣州設計公司https://www.houdianzi.com 我的007辦公資源網站https://www.wode007.com
防護策略
還有一些正則匹配缺陷、業務邏輯型缺陷、配合移動端跳轉等、使用第三方前端框架(比如多媒體編輯框)等場景沒有一一進行說明(精力實在有限了...),后期有空可能會繼續補全這些場景。
檢測的流程就是通過查看代碼是否有document.write、eval、window之類能造成危害的地方,然后通過回溯變量和函數的調用過程,查看用戶是否能控制輸入。如果能控制輸入,就看看是否能復習,能復習就說明存在DOM XSS,需要對輸入的數據進行編碼。
代碼審計時審計的特征點(包括但不限於):
var elements = location.hash; elements.indexOf var oBtn=document.getElementById("Btn"); oBtn.innerHTML oBtn.outerHTML document.createElement oBtn.setAttribute oBtn.appendChild document.write document.writeln eval("var x = '" + location.hash + "'"); setTimeout("alert('xss')", 1000) window.setTimeout document.setTimeout window.setInterval document.execCommand('ForeColor',false,'#BBDDCC'); document.createElement document.createElementNS document.createEvent document.createXxx
js語法很靈活、庫函數也很多,這里沒法完全列舉全,我覺得需要對js語法體系有一定了解,才可能更多的找全這些特征。
當業務需要必須得將用戶輸入的數據放入html,那就要盡量使用安全的方法,比如innerText(),testContent()等。在使用框架時盡量使用框架自帶的安全函數。