XSS
涉及面試題:什麼是 XSS 攻擊?如何防範 XSS 攻擊?什麼是 CSP?
XSS 簡單點來說,就是攻擊者想盡一切辦法將可以執行的代碼注入到網頁中。
XSS 可以分爲多種類型,但是總體上我認爲分爲兩類:持久型和非持久型。
持久型也就是攻擊的代碼被服務端寫入進數據庫中,這種攻擊危害性很大,因爲如果網站訪問量很大的話,就會導致大量正常訪問頁面的用戶都受到攻擊。
舉個例子,對於評論功能來說,就得防範持久型 XSS 攻擊,因爲我可以在評論中輸入以下內容
這種情況如果前後端沒有做好防禦的話,這段評論就會被存儲到數據庫中,這樣每個打開該頁面的用戶都會被攻擊到。
非持久型相比於前者危害就小的多了,一般通過修改 URL 參數的方式加入攻擊代碼,誘導用戶訪問鏈接從而進行攻擊。
舉個例子,如果頁面需要從 URL 中獲取某些參數作爲內容的話,不經過過濾就會導致攻擊代碼被執行
<!-- http://www.domain.com?name=<script>alert(1)</script> -->
<div>{{name}}</div>
但是對於這種攻擊方式來說,如果用戶使用 Chrome 這類瀏覽器的話,瀏覽器就能自動幫助用戶防禦攻擊。但是我們不能因此就不防禦此類攻擊了,因爲我不能確保用戶都使用了該類瀏覽器。
對於 XSS 攻擊來說,通常有兩種方式可以用來防禦。
轉義字符
首先,對於用戶的輸入應該是永遠不信任的。最普遍的做法就是轉義輸入輸出的內容,對於引號、尖括號、斜槓進行轉義
function escape(str) {
str = str.replace(/&/g, '&')
str = str.replace(/</g, '<')
str = str.replace(/>/g, '>')
str = str.replace(/"/g, '&quto;')
str = str.replace(/'/g, ''')
str = str.replace(/`/g, '`')
str = str.replace(/\//g, '/')
return str
}
通過轉義可以將攻擊代碼 <script>alert(1)</script>
變成
// -> <script>alert(1)</script>
escape('<script>alert(1)</script>')
但是對於顯示富文本來說,顯然不能通過上面的辦法來轉義所有字符,因爲這樣會把需要的格式也過濾掉。對於這種情況,通常採用白名單過濾的辦法,當然也可以通過黑名單過濾,但是考慮到需要過濾的標籤和標籤屬性實在太多,更加推薦使用白名單的方式。
const xss = require('xss')
let html = xss('<h1 id="title">XSS Demo</h1><script>alert("xss");</script>')
// -> <h1>XSS Demo</h1><script>alert("xss");</script>
console.log(html)
以上示例使用了 js-xss
來實現,可以看到在輸出中保留了 h1
標籤且過濾了 script
標籤。
CSP
CSP 本質上就是建立白名單,開發者明確告訴瀏覽器哪些外部資源可以加載和執行。我們只需要配置規則,如何攔截是由瀏覽器自己實現的。我們可以通過這種方式來儘量減少 XSS 攻擊。
通常可以通過兩種方式來開啓 CSP:
- 設置 HTTP Header 中的
Content-Security-Policy
- 設置
meta
標籤的方式<meta http-equiv="Content-Security-Policy">
這裏以設置 HTTP Header 來舉例
- 只允許加載本站資源
Content-Security-Policy: default-src ‘self’
- 只允許加載 HTTPS 協議圖片
Content-Security-Policy: img-src https://*
- 允許加載任何來源框架
Content-Security-Policy: child-src 'none'
當然可以設置的屬性遠不止這些,你可以通過查閱 文檔 的方式來學習,這裏就不過多贅述其他的屬性了。
對於這種方式來說,只要開發者配置了正確的規則,那麼即使網站存在漏洞,攻擊者也不能執行它的攻擊代碼,並且 CSP 的兼容性也不錯。
CSRF
涉及面試題:什麼是 CSRF 攻擊?如何防範 CSRF 攻擊?
CSRF 中文名爲跨站請求僞造。原理就是攻擊者構造出一個後端請求地址,誘導用戶點擊或者通過某些途徑自動發起請求。如果用戶是在登錄狀態下的話,後端就以爲是用戶在操作,從而進行相應的邏輯。
舉個例子,假設網站中有一個通過 GET
請求提交用戶評論的接口,那麼攻擊者就可以在釣魚網站中加入一個圖片,圖片的地址就是評論接口
<img src="http://www.domain.com/xxx?comment='attack'"/>
那麼你是否會想到使用 POST
方式提交請求是不是就沒有這個問題了呢?其實並不是,使用這種方式也不是百分百安全的,攻擊者同樣可以誘導用戶進入某個頁面,在頁面中通過表單提交 POST
請求。
如何防禦
防範 CSRF 攻擊可以遵循以下幾種規則:
- Get 請求不對數據進行修改
- 不讓第三方網站訪問到用戶 Cookie
- 阻止第三方網站請求接口
- 請求時附帶驗證信息,比如驗證碼或者 Token
SameSite
可以對 Cookie 設置 SameSite
屬性。該屬性表示 Cookie 不隨着跨域請求發送,可以很大程度減少 CSRF 的攻擊,但是該屬性目前並不是所有瀏覽器都兼容。
驗證 Referer
對於需要防範 CSRF 的請求,我們可以通過驗證 Referer 來判斷該請求是否爲第三方網站發起的。
Token
服務器下發一個隨機 Token,每次發起請求時將 Token 攜帶上,服務器驗證 Token 是否有效。
點擊劫持
涉及面試題:什麼是點擊劫持?如何防範點擊劫持?
點擊劫持是一種視覺欺騙的攻擊手段。攻擊者將需要攻擊的網站通過 iframe
嵌套的方式嵌入自己的網頁中,並將 iframe
設置爲透明,在頁面中透出一個按鈕誘導用戶點擊。
對於這種攻擊方式,推薦防禦的方法有兩種。
X-FRAME-OPTIONS
X-FRAME-OPTIONS
是一個 HTTP 響應頭,在現代瀏覽器有一個很好的支持。這個 HTTP 響應頭 就是爲了防禦用 iframe
嵌套的點擊劫持攻擊。
該響應頭有三個值可選,分別是
DENY
,表示頁面不允許通過iframe
的方式展示SAMEORIGIN
,表示頁面可以在相同域名下通過iframe
的方式展示ALLOW-FROM
,表示頁面可以在指定來源的iframe
中展示
JS 防禦
對於某些遠古瀏覽器來說,並不能支持上面的這種方式,那我們只有通過 JS 的方式來防禦點擊劫持了。
<head>
<style id="click-jack">
html {
display: none !important;
}
</style>
</head>
<body>
<script>
if (self == top) {
var style = document.getElementById('click-jack')
document.body.removeChild(style)
} else {
top.location = self.location
}
</script>
</body>
以上代碼的作用就是當通過 iframe
的方式加載頁面時,攻擊者的網頁直接不顯示所有內容了。
中間人攻擊
涉及面試題:什麼是中間人攻擊?如何防範中間人攻擊?
中間人攻擊是攻擊方同時與服務端和客戶端建立起了連接,並讓對方認爲連接是安全的,但是實際上整個通信過程都被攻擊者控制了。攻擊者不僅能獲得雙方的通信信息,還能修改通信信息。
通常來說不建議使用公共的 Wi-Fi,因爲很可能就會發生中間人攻擊的情況。如果你在通信的過程中涉及到了某些敏感信息,就完全暴露給攻擊方了。
當然防禦中間人攻擊其實並不難,只需要增加一個安全通道來傳輸信息。HTTPS 就可以用來防禦中間人攻擊,但是並不是說使用了 HTTPS 就可以高枕無憂了,因爲如果你沒有完全關閉 HTTP 訪問的話,攻擊方可以通過某些方式將 HTTPS 降級爲 HTTP 從而實現中間人攻擊。
小結
在這一章中,我們學習到了一些常見的前端安全方面的知識及如何防禦這些攻擊。但是安全的領域相當大,這些內容只是滄海一粟,如果大家對於安全有興趣的話,可以閱讀 這個倉庫的內容 來學習和實踐這方面的知識。