XSS 和 CSRF 攻擊的一些非常規防禦方法

一說到安全,大家總會特別敏感,尤其是有相當部分的前端開發者並不了解安全相關的知識,頗有談虎色變的感覺。具體到前端安全這個話題呢,又有些說不清道不明,因為大部分的防禦方案,總少不了後端的參與,也有開發者慢慢覺得好像安全都應該由後端來關注了。

其實不然,起碼 XSS CSRF 這一類的安全問題前端是一定要了解它們的原理和防禦方法的。從防禦方法上來說,XSS 和 CSRF 的防禦在業界都有比較成熟的方案了。本文將記錄一些比較新的防禦方案,可能有一些比較老的書籍或者文章中不會提及這些方法。

XSS

XSS 全稱 Cross Site Scripting ,跨站腳本攻擊,因為 CSS 這名字老早就被樣式表拿走了,大家都在 web 這個領域,重名又不好看,所以只好起了個名字叫 XSS 了。說實話從 XSS 乾的事來講,其實並不太理解為什麼有個「跨站」在裡面,如果一定要強行解釋的話,大概是因為有可能會運行一個來自別人網站的腳本,把這個腳本叫「跨站」了吧。

好了,不重要。

重要的是它是誰,它從哪裡來,要到哪裡去……這樣的哲學問題有點難回答。我們換一個,重要的是它是什麼鬼,能幹什麼,怎麼防。

XSS 是什麼鬼

玩遊戲的人都知道在遊戲中經常出現一些奇葩的名字,比如「星辰並親了他一口」,看上去一臉不知道什麼鬼,但是當他有點事的時候,你就覺得好玩了,比如公會老大邀請他,就會有一條消息「公會老大邀請了星辰並親了他一口」,然後一公會的人哄堂大笑。

其實這種案例用專業的術語來說,就叫 XSS 了……

本來「公會老大邀請了XX」這樣一個句式,只希望XX是一個不會引起誤會的名字而已,結果因為有一個奇葩名字,直接改變了整句話的意思,引介出了意外的含義。

XSS 也是同樣的東西,比如我只想在頁面上顯示一個名字:

<span class="name">{{name}}</span>

但是,如果我的名字是長這樣的:

星辰<script>alert(SB)</script>

這時候就好玩了:

<span class="name">星辰<script>alert(SB)</script></span>

你看,頁面中憑空多了一段腳本。這個例子還算善良的,只是彈出來罵了你一句……

「難道他還能彈出來打我?」

呃……當然不是啦,但是人家可以偷偷幹壞事啊。你說說,你都用JS幹嘛?用戶登錄用的JS吧,讀取資料用的JS吧,點擊買東西、消費用的JS吧,查用戶有多少錢用的JS吧,基於 Cookies 也可以讀寫吧。好的,你用JS能幹的事情人家都能幹。

沒事偷你個登錄態,幫用戶消費兩塊錢,查下你用戶手機號是多少……接下來的事情我就不說了(無非就是前端程序員要背鍋離職唄?)

XSS 怎麼防禦

一個經典的防禦方法就是對內容進行轉義和過濾,比如

var escapeHtml = function(str) { if(!str) return ; str = str.replace(/&/g, &amp;); str = str.replace(/</g, &lt;); str = str.replace(/>/g, &gt;); str = str.replace(/"/g, &quto;); str = str.replace(//g, &#39;); // str = str.replace(/ /g, &#32;); return str;};var name = escapeHtml(`<script>alert(SB)</script>`);

此時 name 會變成

&lt;script&gt;alert(&#39;SB&#39;)&lt;/script&gt;

這樣就會原樣顯示出來,再也無法耍流氓啦。

當然,富文本還要更麻煩一些,因為要保留一部分標籤和屬性,要不然全變純文本了,就不富了。這種情況一般通過黑名單進行過濾,或者白名單放行。即只允許一部分指定的標籤和屬性,其它的全部轉義掉。

CSP 大法

前面轉義的方法的出發點,是讓用戶的輸入不要變成程序,輸入的什麼就讓它輸出成什麼。

事實上現代瀏覽器為我們帶來了一個全新的安全策略,叫作內容安全策略,Content Security Policy,簡稱CSP。CSP的思路跟轉義不一樣,它的著手點是,如果一段代碼變成了程序,我們是否應該運行它。或者更準確一點說,它實際上是定義頁面上哪一些內容是可被信任的,哪一些內容是不被信任的。

因為我們自己的腳本是預先就知道並放在頁面上的,所以我們可以設置好信任關係,當有 XSS 腳本出現時,它並不在我們的信任列表中,因此可以阻止它運行。

它的具體使用方式是在 HTTP 頭中輸出 CSP 策略:

Content-Security-Policy: <policy-directive>; <policy-directive>

從語法上可以看到,一個頭可以輸出多個策略,每一個策略由一個指令和指令對應的值組成。指令可以理解為指定內容類型的,比如script-src指令用於指定腳本,img-src用於指定圖片。值則主要是來源,比如某個指定的URL,或者self表示同源,或者unsafe-inline表示在頁面上直接出現的腳本等。

詳細的指令和值,可以查看MDN相關頁面。

具體到上面的 XSS 例子,可以使用

Content-Security-Policy: script-src self;

這樣除了在同一個域名下的JS文件外,其它的腳本都不可以執行了,自然之前 XSS 的內容也就失效啦。簡單粗暴有沒有?

當然,如果你說,我就是要在頁面中放點內聯的腳本,不可以么?當然可以啦,CSP 設計的時候也考慮了這些情況,還是相當靈活的。你只需要指定一個 nonce 屬性,或者計算一下 hash 值,即可。詳細的用法看 MDN 哦。

說實話,用 CSP 來處理 XSS 攻擊還是不如轉義來得優雅,因為轉義可以不影響用戶輸入輸出,不改變內容的本質。但是 CSP 提供了足夠簡單而又靈活的方式來防禦 XSS ,可以很好地作為我們前端 XSS 防禦的最後一道防線。

CSRF

CSRF 也是個望文生不到義的詞,它的全稱是 Cross Site Request Foggy,即跨站請求攻擊。雖然也有跨站,但我覺得這個跨站還是相當可以理解的,它真的是從別的網站發起一個請求到我們的網站的。

當一個用戶登錄我們的網站後,在 Cookies 中會存放用戶的身份憑證。在大部分時候,就是一個 SessionId 。當用戶下次訪問我們的網站的時候,我們用這個憑證識別出用戶是誰,有沒有登錄態。

如果第三方網站的代碼請求了我們的網站,會發生什麼呢?比如

<img src="http://www.example.com/haha" />

雖然它是一張圖片,但它確實向www.example.com發了一個請求,如此用戶有登錄態的話,其實就相當於是用戶自己發了一個請求。如果這個地址是一個發表文章、發布微博甚至轉賬之類的鏈接,那用戶就在不知情的情況下進行了一些操作。這也是比較嚴重的安全問題。

當然你可能會說,現在誰還這麼弱智,把這麼敏感的操作用 GET 啊?沒錯,你可以選擇用 POST ,但是這絲毫不能阻止 CSRF 攻擊的發生啊。

<iframe name="test"></iframe><form target="test" method="post" action="http://www.example.com/haha"> ...</form>

當這個表單提交的時候,我們就發了一個 POST 請求。華麗麗的 CSRF 。

CSRF 的常規防禦

CSRF 比較常規的防禦方式是通過判斷來源和加 token。

判斷來源比較簡單,主要是判斷referer這個頭,如果不是自己的網站,就返回錯誤。

加 token 即同樣的隨機 token,在 cookies 中放一份,在表單中再放一份。這樣第三方網站就無法獲取到這個 token 是什麼。

但是這樣做也有一個比較明顯的問題,就是無法保證站內用戶的體驗。雖然你防了站外的攻擊,但是也降低了站內用戶的體驗。具體表現在如果同時打開多個表單,只有最後一個表單能成功提交。

same-site 的 Cookie

回想 CSRF 之所以能夠攻擊成功,核心原因就在於用戶的身份是放在 Cookies 中的,而不管你通過什麼方式訪問網站,都會帶上這個網站的 Cookies ,從第三方來的訪問自然也不能例外。

但是,Chrome 在這個問題上給了我們不同的答案,可以放第三方訪問時不帶 Cookies 。也就是說 Cookies 只有本站能用,來自第三方的訪問都不能使用。

具體的使用方式,是在打 Cookie 的時候,加上一個屬性:SameSite,它的值有兩:

  • strict 任何來自第三方的請求都不能使用 Cookies ,包括通過鏈接點進來的
  • lex 只有比較敏感的操作不帶 Cookies ,比如表單提交

針對 CSRF ,我們可以將 Cookies 設置成SameSite: strict的,這樣就可以有效防禦 CSRF 了。不過比較可惜的是,目前只有 Chrome 才支持這一屬性。希望未來所有瀏覽器都能跟上腳步。

使用 SameSite 還會面臨一個問題,如果用戶是點擊鏈接進來的,那麼是不能使用登錄態的。一般可以考慮將用戶不敏感的信息不設置這個屬性,點進來仍然可以顯示當前用戶是誰,但是在請求的時候要求一個比較敏感的 SameSite 的 Cookies。這裡需要更多的實踐經驗來探索。

小結

本文重點講了 XSS 和 CSRF 這兩種比較常見的前端安全問題的防禦思路,尤其是如何使用一些新的規範、實現來幫助我們進行防禦。希望後面瀏覽器對這些安全相關防禦辦法的普及率能再高一些,讓前端工程師能花更少的時間寫出更安全的代碼。

作者: TooooBug

鏈接:imooc.com/article/detai

來源:慕課網

本文原創發佈於慕課網 ,轉載請註明出處,謝謝合作


推薦閱讀:

慕課網:【極力推薦】---安裝阿里代碼規約檢查插件

「乾貨」總結最新的官方穩定版nginxmongomysql快速安裝部署

雙重檢查鎖定與延遲初始化

【Kotlin中使用Dagger2】基礎入門篇(一)

日誌大了,怎麼辦?用我的日誌切割腳本吧!


推薦閱讀:

如何看待黑客軍團第二季結局?
美軍的網路和互聯網是不是物理隔離的?如果是的話,為什麼經常有新聞報道黑客入侵美軍網路?
Anonymous對ISIS正式宣戰會對ISIS造成多大影響?
中國有哪些類似 Hacker News 的網站?
如何看待「神話——信息安全人才顛覆計劃」?

TAG:網路安全 | 黑客Hacker | 網路防禦 |