跨站腳本攻擊(XSS)是客戶端腳本安全的頭號大敵。本文章深入探討 XSS 攻擊原理,下一章(XSS 攻擊進階)將深入討論 XSS 進階攻擊方式。
本系列將持續更新。
XSS 簡介
XSS(Cross Site Script),全稱跨站腳本攻擊,為了與 CSS(Cascading Style Sheet) 有所區別,所以在安全領域稱為 XSS。
XSS 攻擊,通常指黑客通過 HTML 注入 篡改網頁,插入惡意腳本,從而在用戶瀏覽網頁時,控制用戶瀏覽器的一種攻擊行為。在這種行為最初出現之時,所有的演示案例全是跨域行為,所以叫做 "跨站腳本" 。時至今日,隨着Web 端功能的復雜化,應用化,是否跨站已經不重要了,但 XSS 這個名字卻一直保留下來。
隨着 Web 發展迅速發展,JavaScript 通吃前后端,甚至還可以開發APP,所以在產生的應用場景越來越多,越來越復雜的情況下, XSS 愈來愈難統一針對,現在業內達成的共識就是,針對不同的場景而產生的不同 XSS ,需要區分對待。可即便如此,復雜應用仍然是 XSS 滋生的溫床,尤其是很多企業實行迅捷開發,一周一版本,兩周一大版本的情況下,忽略了安全這一重要屬性,一旦遭到攻擊,后果將不堪設想。
那什么是 XSS 呢?我們看下面一個例子。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>XSS</title>
</head>
<body>
<div id="t"></div>
<input id="s" type="button" value="獲取數據" onclick="test()">
</body>
<script>
function test() {
// 假設從后台取出的數據如下
const arr = ['1', '2', '3', '<img src="11" onerror="alert(\'我被攻擊了\')" />']
const t = document.querySelector('#t')
arr.forEach(item => {
const p = document.createElement('p')
p.innerHTML = item
t.append(p)
})
}
</script>
</html>
這個時候我們在頁面上點擊 獲取數據
按鈕時,頁面上會出現如下信息:
你會發現,本應該作為數據展示在界面上的內容居然執行了,這顯然是開發者不希望看到的。
XSS 攻擊類型
XSS 根據效果的不同可以分為如下幾類:
反射型 XSS
簡單來說,反射型 XSS 只是將用戶輸入的數據展現到瀏覽器上(從哪里來到哪里去),即需要一個發起人(用戶)來觸發黑客布下的一個陷阱(例如一個鏈接,一個按鈕等),才能攻擊成功,一般容易出現在搜索頁面、留言板塊。這種反射型 XSS 也叫做 非持久型 XSS(No-persistent XSS) 。
例如:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
</head>
<body>
<div id="t"></div>
<input id="s" type="button" value="獲取數據" onclick="test()">
</body>
<script>
function test() {
const arr = ['1', '2', '3', '<img src="11" onerror="console.log(window.localStorage)" />']
const t = document.querySelector('#t')
arr.forEach(item => {
const p = document.createElement('p')
p.innerHTML = item
t.append(p)
})
}
</script>
</html>
假設這是一個留言板塊,加載到這一頁時,頁面會輸出:
黑客可以輕易盜取存儲在你本地瀏覽器的各種信息,進而模擬登陸信息,黑入賬戶,進行各種操作。
存儲型 XSS
存儲型 XSS 會把用戶輸入的數據 保存 在服務器端,這種 XSS 十分穩定,十分有效,效果持久。存儲型 XSS 通常叫做 "持久型 XSS(Persistent XSS)",即存在時間比較長。
比較常見的場景就是,黑客寫下一篇包含惡意代碼的文章,文章發表后,所有訪問該博客文章的用戶都會執行這一段代碼,進行惡意攻擊。
例如:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
</head>
<body>
<div id="t">
這是我寫的一篇文章
</div>
</body>
<script>
console.log(navigator.userAgent)
</script>
</html>
直接輸出了瀏覽器信息,黑客可以獲取到這些信息后,發送到自己的服務器,隨意操作。
DOM Based XSS
實際上,這種類型的 XSS 與是否存儲在服務器端無關,從效果上來說也是反射型 XSS,單獨划分出來是因為此類 XSS 形成的原因比較特殊。
簡單來說,通過修改頁面 DOM 節點形成的 XSS,稱之為 DOM Based XSS。
例子如下:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>XSS</title>
</head>
<body>
<div id="t"></div>
<input type="text" id="text" value="">
<input type="button" id="s" value="search" onclick="test()">
</body>
<script>
function test() {
const str = document.querySelector('#text').value
document.querySelector('#t').innerHTML = '<a href="' + str + '" >查找結果</a>'
}
</script>
</html>
該頁面的作用是,在輸入框內輸入一個內容,跳出查找結果能直接跳轉,效果如下:
點擊查找結果后,頁面會自動跳到百度(毒)頁面,但是細心的我們會發現,這字符串拼接有可乘之機啊,輸入" onclick=alert(/XSS/) //
:
果然,頁面執行了我們輸入的東西,上面的內容是,第一個雙引號閉合掉href
的第一個雙引號,然后插入onclick
事件,最后注釋符 //
注釋掉第二個雙引號,點擊跳轉鏈接,腳本就被執行了。