觀察如下代碼,它們若是做爲html的節點內容,就會引發xss攻擊javascript
<p><svg onload=prompt(/xss/)></p>
<iframe src="http://www.baidu.com" height="250" width="300"></iframe>
<a href="javascript:alert('xss')">你好</a>
<img src=1 onerror=alert('xss')>
複製代碼
所以,XSS 攻擊,通常是指攻擊者經過在網頁中注入惡意腳本,當用戶瀏覽網頁時,惡意腳本執行,控制用戶瀏覽器行爲的一種攻擊方式。html
而後談談跨站這個詞: 咱們通常但願網站運行的全部邏輯均來自本站,當本站運行了其餘網站的東西,例如常見的js腳本,就可能產生跨站腳本攻擊,固然在這裏咱們能夠理解爲不侷限於遠程腳本,由於一般探測是否頁面是否存在潛在的xss攻擊,都會使用一個alert框,也被稱作xss探測器。舉個簡單的例子:前端
例子1
// server.js
const express = require('express');
const app = express();
app.get('/', function(req, res){
res.send('hello world')
})
app.get('/index', function(req, res){
const id = req.query.id
res.send(`<textarea>${id}</textarea>`)
})
app.listen(3001)
複製代碼
在瀏覽器輸入網址 localhost:3001/index?id=</textarea><script>alert(1)</script>
java
按下回車的那一瞬,並沒看到預期的彈框,心裏一陣失落,但同時也注意到了textarea輸入框並無東西,因而想莫非是谷歌瀏覽器已經作了什麼事情,來預防此種簡易的攻擊,打開console面板 git
果真,好吧,輸給了chrome,我仍是心服口服的,在這裏值得提一下,谷歌的安全機制仍是很完善的,這種簡單的反射性跨站攻擊,谷歌直接就幫忙處理攔截了,safari也會對這種簡易的攻擊作基本的攔截。 github
好吧,好大一個框! chrome
本質: 插值形成的注入。數據庫
腳本程序: <textarea>${id}</textarea>
express
數據源 | 渲染結果 |
---|---|
id: 1 | <textarea>1</textarea> |
id: </textarea><script>alert(1)</script> |
<textarea></textarea><script>alert(1)</script></textarea> |
在這個例子中,攻擊者首先把盛放數據的容器提早閉合,而後經過script標籤爲數據賦予行爲,讓數據源再也不單純,改變整個程序的邏輯,從而實現攻擊的目的
後端
說到這裏,咱們已經用了個簡單的例子引出了今天的主題xss,也許有人內心會有疑問,不就是彈個框嗎?果然如此嗎?咱們能夠換個角度考慮一下,咱們平時寫的腳本能幹什麼?
獲取頁面數據
獲取cookie
劫持前端的邏輯
發送請求
...
所以,xss也能夠幹這些事,只有你想不到,沒有黑客作不到的,攻擊者經過注入腳本,能夠經過dom抓取頁面中的數據,也能夠經過document.cookie獲取用戶的cookie,通常cookie保存有用戶登錄態,攻擊者只須要將這個cookie種在本身的瀏覽器中,就能夠獲取用戶的各類信息,模擬用戶進行各類操做等。
上網搜了下以前被xss惡意攻擊的案例,發現了一個典型,做爲前端工程師,或多或少都知道站酷這個網站,這是國內很是有名的設計師交流平臺,在官網首頁有個搜索功能,當輸入搜索關鍵詞後點擊搜索,關鍵詞會在搜索結果頁二次顯示 站酷
值得說明的是:如今站酷已經修復了,目前的效果是對標籤進行了處理
我理解的三者的區別:
經過url連接點擊觸發,是一次性行爲,實質是服務器端沒有對用戶的惡意輸入作安全處理,直接反射相應內容。文章開始的例子1就是一個典型的反射型xss攻擊,這類攻擊能夠經過url辨別,谷歌這類安全性高的瀏覽器,基本能夠自動處理這類攻擊
顧名思義,存儲,通常涉及後端數據存儲,常見的場景就是APP的意見反饋模塊,前端經過接口把用戶輸入的信息傳給後端,當有些後臺管理系統須要展現反饋意見時,就會取數據庫中的這些數據,當這些數據中含有攻擊的腳本,那就形成了存儲型xss攻擊。這種攻擊是持久性的。
客戶端的腳本程序能夠動態地檢查和修改頁面內容,而不依賴於服務器端的數據。可能引發dom型xss的:使用innerHTML, documen.write屬性...
<img src="null" onerror='alert(document.cookie)' />
複製代碼
// test.html
<script>
const info = '';alert(1);''
const data = `hello${info}`
</script>
複製代碼
X-XSS-Protection: Chrome 和 Safari 的一項功能,可在檢測到反射的跨站點腳本(XSS)攻擊時阻止頁面加載 可選的值:
將標籤符號'<', '>'全局轉義
...
const escapeHtml = (str) => {
str = str.replace(/&/g, '&')
.replace(/</g, '<')
.replace(/>/g, '>')
.replace(/"/g, '&quto;') .replace(/'/g, ''') .replace(/ /g, ' ') return str } ... app.get('/index', (req, res) => { const id = req.query.id || '' res.send(`<textarea>${escapeHtml(id)}</textarea>`) }) 複製代碼
輸出:
高度定製要展現的內容,這裏我使用了cheerio來將html字符串解析成html實體, cheerio
// <img src="123" onerror="alert(1)"></img>使用cheerio的輸出
{ type: 'tag',
name: 'img',
attribs: { src: '123', onerror: 'alert(1)' },
children: [],
next: null,
prev: null,
parent: null,
root:
{ type: 'root',
name: 'root',
attribs: {},
children: [ [Circular] ],
next: null,
prev: null,
parent: null
}
}
複製代碼
// server.js 無關代碼已省略
...
const whiteList = {
'img': ['src']
}
const xssFilter = (html) => {
if (!html) return ''
const $ = cheerio.load(html, {
normalizeWhitespace: true,
xmlMode: true
})
$('*').each((index, elem) => {
if (!whiteList[elem.name]) {
$(elem).remove()
return
}
for (var attr in elem.attribs) {
if (whiteList[elem.name].indexOf(attr) === -1) {
$(elem).attr(attr, null)
}
}
})
onsole.log(html, $.html())
return $.html()
}
app.get('/api', (req, res) => {
const cb = (result) => {
result = result.map(item => {
const { comment, name, time } = item
return {
name,
time,
comment: xssFilter(comment)
}
})
res.send({
data: result,
message: 'success',
status: 1
})
}
model.find({}, cb) // 自定義的查詢方法
})
...
複製代碼
過濾結果: 由於白名單隻配置了:'img': ['src'], 所以img的onerror屬性被過濾,其餘不在白名單的標籤總體被過濾
csp是個http的頭,實質是用於規定內容是否可執行,所以只要將用戶的內容標記爲不可執行,就ok了~~~
// server.js
...
app.use((req, res, next) => {
res.set({
'Content-Security-Policy': "default-src 'self'"
})
next()
})
...
複製代碼
沒有攻擊成功