之前公司的安全組掃出了我們官網(wǎng)的幾個安全漏洞。安全問題對很多前端來說都是只聽過,沒見過。這次遇到了真實(shí)對漏洞,可以做個記錄。
存儲型XSS漏洞
XSS,跨站腳本攻擊。存儲型XSS攻擊,即攻擊代碼被當(dāng)成文本(如帖子)提交保存到服務(wù)器,其他用戶打開這段文本的時候,代碼就會在其他用戶的瀏覽器里執(zhí)行。
場景:一個壞人在論壇發(fā)布帖子 內(nèi)容如下:
大家好,第一次在這個論壇發(fā)帖,現(xiàn)在我要植入一段代碼:<img src=x onerror=alert(1)>
如果論壇沒有對帖子的內(nèi)容做轉(zhuǎn)義處理,直接發(fā)布出去。 其他用戶點(diǎn)擊這個帖子,頁面就會插入這個圖片標(biāo)簽。由于圖片的src異常,圖片加載失敗,就會觸發(fā)onerror里的alert。
如果onerror里的內(nèi)容不是一個簡單的alert,而是一段盜取用戶cookies的代碼呢?那么就會對用戶對賬戶造成威脅。
防御措施
對< > / 符號做escape轉(zhuǎn)義即可,escape可以把所有ASCII 之外的所有字符轉(zhuǎn)換為 %xx 或 %uxxxx(x表示十六進(jìn)制的數(shù)字)的轉(zhuǎn)義序列。
延伸:vue 中 v-html 指令的合理使用
用過 vue 的同學(xué)都知道,v-html 的作用是可以把填入 v-html 里的值轉(zhuǎn)化為 html 代碼,所以 v-html可能會成為攻擊者利用的漏洞,如果把用戶提交的內(nèi)容使用 v-html渲染,就可能會把用戶輸入的內(nèi)容變成可執(zhí)行的代碼,形成 XSS 攻擊。
反射型XSS漏洞
反射型漏洞:攻擊者通過特定的方式來誘惑受害者去訪問一個包含惡意代碼的URL。
場景還是壞人在論壇發(fā)帖,壞人在帖子里貼一個URL,其他用戶訪問這個URL,使URL上的代碼在用戶瀏覽器里被執(zhí)行。
可能有人會問,在帖子里附帶鏈接不是正?,F(xiàn)象嗎,用戶點(diǎn)擊第三方鏈接導(dǎo)致被攻擊跟我有什么關(guān)系?
那么,如果用戶點(diǎn)擊的就是官方鏈接而觸發(fā)了XSS攻擊呢?
場景:這個發(fā)生場景現(xiàn)在來看有點(diǎn)古老了,但大部分網(wǎng)站可能仍會使用的跨域策略:JSONP。
簡單描述JSONP的流程:前端使用<script>標(biāo)簽請求后端資源,后端返回的資源會被一個函數(shù)包裹,類似:
callback({
a: 1,
b: 2
});
而前端會提前在頁面定義好這個callback函數(shù),例如:
// 請求到達(dá)頁面,觸發(fā) callback 函數(shù),將資源掛到window上
function callback(res) {
window.res = res;
}
而通常后臺返回的函數(shù)名并不會固定返回callback,因為可能會調(diào)用到頁面其他叫callback的函數(shù),所以,前端在請求的時候,都會在鏈接后帶一個參數(shù),告訴后臺,返回的函數(shù)名應(yīng)該叫什么,例如:
前端請求地址:https://xxxxx/giftjson?jsoncallback=mycallback
后臺返回結(jié)果:
// jsoncallback傳的參數(shù)是什么,返回的函數(shù)名就是什么
mycallback({
a: 1,
b: 2
});
可以看到,請求地址上的jsoncallback參數(shù)是什么,后臺返回的函數(shù)名就是什么。看到這句話是不是隱隱感覺到不對勁?如果jsoncallback參數(shù)是一段代碼呢?
假設(shè),壞人發(fā)現(xiàn)了斗魚直播的一個接口有這個漏洞,在斗魚論壇發(fā)帖:
斗魚出了個活動啦,大家點(diǎn)擊這個鏈接可以免費(fèi)抽獎,這個是斗魚官方鏈接大家放心點(diǎn):
https://www.douyu.com/getUserInfo?jsoncallback=<img+src=x+onerror=alert(1)>
其他用戶發(fā)現(xiàn)這個確實(shí)是斗魚的官方鏈接就點(diǎn)了進(jìn)去,結(jié)果就運(yùn)行了jsoncallback里的代碼。
防御措施
個攻擊其實(shí)還需要一點(diǎn)機(jī)緣巧合,比如把返回頭的Content-Type設(shè)置成了application/javascript或者text/html,就會導(dǎo)致字符被當(dāng)作代碼運(yùn)行。所以防止反射型攻擊的方法就是將返回頭的Content-Type設(shè)置為application/json;即可。
CSRF 攻擊
CSRF,跨站點(diǎn)請求偽造。即攻擊者在他自己的網(wǎng)站調(diào)用你的接口,達(dá)到修改你的網(wǎng)站數(shù)據(jù)的目的。
場景:用戶在斗魚論壇設(shè)置了“記住我的登錄狀態(tài)”,于是 douyu.com這個域名下保存了用戶的登錄cookies。攻擊者注意到斗魚修改用戶資料的接口douyu.com/updateUserInfo有CSRF漏洞,于是他偽造了一個斗魚論壇網(wǎng)站,并做了一個表單:
<html>
<body>
<form id="csrf" action="douyu.com/updateUserInfo" method="POST">
<input type="hidden" name="gender" value="2" />
<input type="hidden" name="hometown" value="1234" />
<input type="hidden" name="birthday" value="2020-06-11" />
<input type="hidden" name="nick_name" value="1234" />
<input type="hidden" name="st" value="123" />
</form>
</body>
<script type="text/javascript">
function autoSubmit() {
document.getElementById("csrf").submit();
}
autoSubmit()
</script>
</html>
這個表單會提交到douyu.com/updateUserInfo接口,由于用戶的瀏覽器保存了登錄的cookies,請求發(fā)送出去的時候,用戶的登錄態(tài)也會帶出去,斗魚驗證登錄態(tài)后,確認(rèn)了是用戶本人操作,允許了本次修改。
我們可以發(fā)現(xiàn),通過CSRF漏洞,攻擊者偽造了用戶的登錄態(tài),使修改用戶資料的操作成功了。如果本次操作不是修改用戶操作,而是轉(zhuǎn)賬等危害用戶資產(chǎn)安全的操作呢?
防御措施
由此可見,涉及資料修改的操作,僅驗證用戶登錄態(tài)是不夠的。還需要其他的驗證手段:
初級防御措施:判斷請求的Referer頭
請求中的Referer頭用于判斷該請求是在哪個頁面的基礎(chǔ)上發(fā)出去的,比如a.com請求了一張圖片douyu.com/1.jpg,那么圖片的Referer頭就為:Referer: a.com。
上面的攻擊場景中,服務(wù)器可以判斷請求的Referer頭,判斷該請求是否是官方網(wǎng)站douyu.com發(fā)出的,如果不是,則說明是第三方調(diào)用,禁止修改。
但是問題是,請求的請求頭是可以修改的,攻擊者只要將請求的Referer頭手動修改為douyu.com,就繞過了這層防御。
進(jìn)階防御措施:
token校驗
每個修改資料的請求,都必須攜帶一個token,而這個token只能在官方網(wǎng)站下生成,提交修改操作的時候,校驗這個token是否正確,只有正確才允許修改資料。
這種驗證又叫做“加鹽”驗證。在表單中混入一個做校驗的表單項,用來給服務(wù)器做安全校驗。cookies設(shè)置Samesite
設(shè)置保存登錄態(tài)的 cookies 只能在同一個域名的頁面下才會隨著請求一起發(fā)送。這樣可以保證cookies不會被第三方使用。但是也多了一個束縛是登錄態(tài)不能被其他我們受信任的域名共享。
比如很多單點(diǎn)登錄的實(shí)現(xiàn)(即如果你在taobao.com登錄了,你訪問tmall.com的時候,會發(fā)現(xiàn)也是登錄著的)就是基于 cookies 共享,可能就會收到影響。