為什么你的網(wǎng)頁需要CSP?
內(nèi)容安全策略(CSP)是一個 HTTP Header,CSP 通過告訴瀏覽器一系列規(guī)則,嚴格規(guī)定頁面中哪些資源允許有哪些來源, 不在指定范圍內(nèi)的統(tǒng)統(tǒng)拒絕。
使用它是防止跨站點腳本(XSS)漏洞的最佳方法。由于難以使用 CSP 對現(xiàn)有網(wǎng)站進行改造(可通過漸進式的方法),因此 CSP 對于所有新網(wǎng)站都是強制性的,強烈建議對所有現(xiàn)有高風(fēng)險站點進行 CSP 策略配置。
為什么要配置
CSP 的主要好處就是可以全面禁止使用不安全的嵌入式 JavaScript。內(nèi)聯(lián) JavaScript(無論是反射的還是存儲的),意味著不正確的轉(zhuǎn)義用戶輸入都可以被 Web 瀏覽器解釋為 JavaScript 代碼。通過使用 CSP 禁用嵌入式 JavaScript,你可以有效消除針對你站點的幾乎所有 XSS 攻擊。
注意,禁用內(nèi)聯(lián) JavaScript 意味著必須從 src 標記加載所有 JavaScript <script>。直接在標記上使用的事件處理程序(例如 onclick )將無法正常工作,<script>標記內(nèi)的 JavaScript 也會通過。此外,使用 <style> 標簽或 style 屬性的內(nèi)聯(lián)樣式表也將無法加載。因此為了讓 CSP 易于實現(xiàn),在設(shè)計站點時必須非常小心。
如何配置?
開啟 CSP 很簡單, 你只需要配置你的網(wǎng)絡(luò)服務(wù)器返回 Content-Security-Policy 這個 HTTP Header (有時你會看到一些關(guān)于X-Content-Security-Policy Header 的提法, 那是舊版本,你無須再如此指定它)。
除此之外,<meta> 元素也可以被用來配置該策略, 例如
- <meta http-equiv="Content-Security-Policy" content="default-src 'self'; img-src https://*; child-src 'none';">
指令
無論是 header ,還是在 <meta> 標簽中指定,其值的格式都是統(tǒng)一的,由一系列 CSP 指令(directive)組合而成。
- Content-Security-Policy: <policy-directive>; <policy-directive>
這里 directive,即指令,是 CSP 規(guī)范中規(guī)定用以詳細詳述某種資源的來源,比如前面示例中使用的 script-src,指定腳本可以有哪些合法來源,img-src 則指定圖片的合法淶源,以下是常用指令:
- base-uri 限制可出現(xiàn)在頁面 <base> 標簽中的鏈接。
- child-src 列出可用于 worker 及以 frame 形式嵌入的鏈接。譬如: child-src https://youtube.com 表示只能從 Youtube 嵌入視頻資源。
- connect-src 可發(fā)起連接的地址 (通過 XHR, WebSockets 或 EventSource)。
- font-src 字體來源。譬如,要使用 Google web fonts 則需要添加 font-src https://themes.googleusercontent.com 規(guī)則。
- form-action <form>標簽可提交的地址。
- frame-ancestors 當(dāng)前頁面可被哪些來源所嵌入(與 child-src 正好相反)。作用于 <frame>, <iframe>, <embed>及 <applet>。該指令不能通過 <meta>指定且只對非 HTML文檔類型的資源生效。
- frame-src 該指令已在 level 2 中廢棄但會在 level 3 中恢復(fù)使用。未指定的情況下回退到 tochild-src 指令。
- img-src 指定圖片來源。
- media-src 限制音視頻資源的來源。
- object-src Flash 及其他插件的來源。
- plugin-types 限制頁面中可加載的插件類型。
- report-uri 指定一個可接收 CSP 報告的地址,瀏覽器會在相應(yīng)指令不通過時發(fā)送報告。不能通過 <meta> 標簽來指定。
- style-src 限制樣式文件的來源。
- upgrade-insecure-requests 指導(dǎo)客戶端將頁面地址重寫,HTTP 轉(zhuǎn) HTTPS。用于站點中有大量舊地址需要重定向的情形。
- worker-src CSP Level 3 中的指令,規(guī)定可用于 worker, shared worker, 或 service worker 中的地址。
預(yù)設(shè)值
除了配置指定的淶源以外,這些指令還可以配置一些預(yù)定義的值來完成一些默認配置:
- none 不匹配任何東西。
- self 匹配當(dāng)前域,但不包括子域。比如 example.com 可以,api.example.com 則會匹配失敗。
- unsafe-inline 允許內(nèi)嵌的腳本及樣式。是的,沒看錯,對于頁面中內(nèi)嵌的內(nèi)容也是有相應(yīng)限制規(guī)則的。
- unsafe-eval 允許通過字符串動態(tài)創(chuàng)建的腳本執(zhí)行,比如 eval,setTimeout 等。
如果頁面中非得用內(nèi)聯(lián)的寫法,還有種方式。即頁面中這些內(nèi)聯(lián)的腳本或樣式標簽,賦值一個加密串,這個加密串由服務(wù)器生成,同時這個加密串被添加到頁面的響應(yīng)頭里面。
- <script nonce="EDNnf03nceIOfn39fn3e9h3sdfa">
- // 這里放置內(nèi)聯(lián)在 HTML 中的代碼
- </script>
頁面 HTTP 響應(yīng)頭的 Content-Security-Policy配置中包含相同的加密串:
- Content-Security-Policy: script-src 'nonce-EDNnf03nceIOfn39fn3e9h3sdfa'
配置示例
示例 1
所有內(nèi)容均來自站點的同一個源 (不包括其子域名)
Content-Security-Policy: default-src 'self'
示例 2
允許內(nèi)容來自信任的域名及其子域名 (域名不必須與CSP設(shè)置所在的域名相同)
Content-Security-Policy: default-src 'self' *.trusted.com
示例 3
允許網(wǎng)頁應(yīng)用的用戶在他們自己的內(nèi)容中包含來自任何源的圖片, 但是限制音頻或視頻需從信任的資源提供者(獲得),所有腳本必須從特定主機服務(wù)器獲取可信的代碼.
Content-Security-Policy: default-src 'self'; img-src *; media-src media1.com media2.com; script-src userscripts.example.com
在這里,各種內(nèi)容默認僅允許從文檔所在的源獲取, 但存在如下例外:
- 圖片可以從任何地方加載(注意 "*" 通配符)。
- 多媒體文件僅允許從 media1.com 和 media2.com 加載(不允許從這些站點的子域名)。
- 可運行腳本僅允許來自于userscripts.example.com。
示例 4
一個線上銀行網(wǎng)站的管理者想要確保網(wǎng)站的所有內(nèi)容都要通過SSL方式獲取,以避免攻擊者竊聽用戶發(fā)出的請求。
Content-Security-Policy: default-src https://onlinebanking.jumbobank.com
該服務(wù)器僅允許通過HTTPS方式并僅從onlinebanking.jumbobank.com域名來訪問文檔。
示例 5
一個在線郵箱的管理者想要允許在郵件里包含HTML,同樣圖片允許從任何地方加載,但不允許JavaScript或者其他潛在的危險內(nèi)容(從任意位置加載)。
Content-Security-Policy: default-src 'self' *.mailsite.com; img-src *
注意這個示例并未指定script-src。在此CSP示例中,站點通過 default-src 指令的對其進行配置,這也同樣意味著腳本文件僅允許從原始服務(wù)器獲取。
上報你的數(shù)據(jù)
當(dāng)檢測到非法資源時,除了控制臺看到的報錯信息,也可以讓瀏覽器將日志發(fā)送到服務(wù)器以供后續(xù)分析使用。接收報告的地址可在 Content-Security-Policy 響應(yīng)頭中通過 report-uri指令來配置。當(dāng)然,服務(wù)端需要編寫相應(yīng)的服務(wù)來接收該數(shù)據(jù)。
配置 report-uri
- Content-Security-Policy: default-src 'self'; ...; report-uri /my_amazing_csp_report_parser;`
服務(wù)端收到請求:
- {
- "csp-report": {
- "document-uri": "http://example.org/page.html",
- "referrer": "http://evil.example.com/",
- "blocked-uri": "http://evil.example.com/evil.js",
- "violated-directive": "script-src 'self' https://apis.google.com",
- "original-policy": "script-src 'self' https://apis.google.com; report-uri http://example.org/my_amazing_csp_report_parser"
- }
- }
Report Only
CSP 提供了一種報告模式,該模式下資源不會真的被限制加載,只會對檢測到的問題進行上報 ,以 JSON 數(shù)據(jù)的形式發(fā)送到 report-uri 指定的地方。
通過指定 Content-Security-Policy-Report-Only 而不是 Content-Security-Policy,則開啟了報告模式。
- Content-Security-Policy-Report-Only: default-src 'self'; ...; report-uri /my_amazing_csp_report_parser;
當(dāng)然,你也可以同時指定兩種響應(yīng)頭,各自里的規(guī)則還會正常執(zhí)行,不會互相影響。比如:
- Content-Security-Policy: img-src *;
- Content-Security-Policy-Report-Only: img-src ‘none’; report-uri http://reportcollector.example.com/collector.cgi
這里圖片還是會正常加載,但是 img-src ‘none’ 也會檢測到并且發(fā)送報告。
報告模式對于測試非常有用。在開啟 CSP 之前肯定需要對整站做全面的測試,將發(fā)現(xiàn)的問題及時修復(fù)后再真正開啟,比如上面提到的對內(nèi)聯(lián)代碼的改造。
如何檢驗配置成功了?
在Network中可以看到配置成功的header:
下面是 Twitter 的一個配置示例,非常完善:
在控制臺可以看到資源 block 報錯:
Network中可以看到Block資源上報: