淺析XSS與CSRF
在 Web 安全方面,XSS 與 CSRF 可以說是老生常談了。
XSS
XSS,即 cross site script,跨站腳本攻擊,縮寫原本為 CSS,但為了和層疊樣式表(Cascading Style Sheet)區分,改為 XSS。
XSS 攻擊是指攻擊者在網站上注入惡意腳本,使用戶在瀏覽使用網頁時進行惡意操作,注入腳本除了 JavaScript,還會有 CSS、HTML 等等。
XSS 攻擊可以分為 3 種:反射型(非持久型)、存儲型(持久型)、DOM型。
反射型
反射型的 XSS 攻擊方式一般是攻擊者誘使用戶進行點擊惡意鏈接、提交表單等等操作,從而達到注入腳本的目的。
舉個例子,假設某頁面會直接把搜索關鍵詞拼接到 HTML 頁面上顯示出來:
<p>您搜索的關鍵詞是:<?php showKeyword(keyword) ?></p>
當用戶點擊搜索后,會向服務端發送這樣的請求http://example/search?keyword=xxx
,然后服務端把 keyword 后面的字符進行 HTML 拼接然后返回。
這時,假如用戶點擊了一個惡意鏈接http://example/search?keyword=<script>xss攻擊腳本</script>
,那返回的 HTML 就會變成這樣:
<p>您搜索的關鍵詞是:<script>xss攻擊腳本</script></p>
然后瀏覽器就會執行 script 里面 xss 攻擊腳本,從而達到攻擊者的攻擊目的。
存儲型
存儲型的 XSS 攻擊一般是攻擊者將惡意代碼作為輸入的數據提交給服務端,然后服務端存儲到數據庫內,當有用戶請求這個數據后,服務端返回的數據其實是惡意代碼,從而發生 XSS 攻擊。
比較常見的就是在論壇發帖、評論,然后在里面注入惡意代碼,當用戶點擊帖子的時候,就會受到攻擊。
DOM型
DOM型的 XSS 攻擊一般是攻擊者在注入惡意腳本后,修改頁面元素,獲取 DOM 的數據然后執行攻擊操作。比如將用戶的登錄表單的提交換成攻擊者的服務端,從而獲取用戶的登錄信息。
防范 XSS
-
CSP 內容安全策略,主流瀏覽器都實現了 CSP。
配置內容安全策略涉及到添加 Content-Security-Policy HTTP頭部到一個頁面,並配置相應的值,以控制用戶代理(瀏覽器等)可以為該頁面獲取哪些資源。比如一個可以上傳文件和顯示圖片頁面,應該允許圖片來自任何地方,但限制表單的action屬性只可以賦值為指定的端點。一個經過恰當設計的內容安全策略應該可以有效的保護頁面免受跨站腳本攻擊。 (MDN)
-
通過設置 HttpOnly 防止 Cookie 被讀取。
-
前端對用戶的輸入進行檢查轉義,建立白名單,只允許安全的字符和 HTML 標簽存在:
const HTML_DECODE = { ' ': '\n', '<' : '<', '>' : '>', '&' : '&', '"': '"', ' ': ' ', '"': '\' // more code };
-
前端邏輯比較容易被繞過,所以后端也要對接收的數據進行檢查過濾,並在輸出或者拼接 HTML 的時候進行編碼、轉義。
CSRF
CSRF,即 cross site request forgery,跨站請求偽造,可以理解為重放攻擊。比較常見的情況是,攻擊者誘使用戶打開釣魚網站進行一些操作。
還是舉一個例子來說明:假設某用戶登錄了www.a.com
網站,然后在沒有登出的情況下打開了釣魚網站www.b.com
,而釣魚網站里面有惡意腳本,在被打開的時候就帶着 Cookie 信息向www.a.com
發送了跨域請求。
CSRF 的特點
- CSRF 發起請求方式很多:圖片URL、超鏈接、表單提交等。
- 與 XSS 攻擊不同, CSRF 一般在第三方網站發起攻擊。
- CSRF 只是利用 http 請求自動攜帶用戶的 Cookie 信息來通過驗證,並不能獲取 Cookie 信息。
CSRF 防范
對於重放攻擊,其實前端是沒什么辦法進行處理的,主要靠后端。
-
Referer 字段
在 http 的請求頭里,有一個 Referer 字段,記錄了 http 請求的來源地址,服務端通過檢查這個字段,判斷請求是否來自合法地址。但需要注意的是,Referer 字段是由瀏覽器來進行添加的,所以存在被篡改的可能。
-
驗證機制
通常 CSRF 是在用戶不知情的情況下發送請求進行攻擊的,所以可以利用驗證機制強制用戶與網站進行交互,例如發送驗證碼到用戶的手機、郵箱,或者需要用戶拼圖、填寫驗證碼等。但驗證機制對於用戶體驗不太好,而且並不是所有操作都能加上驗證的。
-
token
服務端在用戶登錄后,簽發一個隨機的 token,可以埋在頁面中,也可以存儲在 sessionStorage 中,然后在客戶端設置攔截器,為所有請求加上 token,服務端再設置攔截器,檢查請求是否擁有 token。因為同源策略(協議、域名、端口相同為同源)的原因,CSRF 並不能拿到 token。
let xhr = new XMLHttpRequest(); let token = sessionStorage.getItem('csrfToken'); xhr.setRequestHeader('CSRF-Token', token);
但需要注意的是, token 需要存儲在服務端,需要服務端進行讀取、驗證 token。