當我不經意間在 Twitter 頁面 <!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<title>Twitter</title>
<style>
body {
background-color: #ffffff;
font-family: sans-serif;
}
a {
color: #1da1f2;
}
svg {
color: #1da1f2;
display: block;
fill: currentcolor;
height: 21px;
margin: 13px auto;
width: 24px;
}
</style>
</head>
<body>
<noscript>
<center>If you’re not redirected soon, please <a href="/">use this link</a>.</center>
</noscript>
<script nonce="SG0bV9rOanQfzG0ccU8WQw==">
document.cookie = "app_shell_visited=1;path=/;max-age=5";
location.replace(location.href.split("#")[0]);
</script>
</body>
</html>
相比平時看到的其他站點的原始碼,可以說是很清爽了。沒有亂七八糟的標籤,功能卻一樣不少。特別有迷惑性,以為這便是頁面所有的原始碼,但檢視 DevTools 的 Source 皮膚後很容易知道這並不是真實的 HTML 程式碼。但為何頁面原始碼給出的是如此清爽的版本,這裡先不研究。 把目光移向 script 標籤時,發現一個不認識的 ! <script nonce="SG0bV9rOanQfzG0ccU8WQw==">
document.cookie = "app_shell_visited=1;path=/;max-age=5";
location.replace(location.href.split("#")[0]);
</script>
Content Security Policy (CSP)要了解 我們都知道瀏覽器有同源策略(same-origin policy)的安全限制,即每個站點只允許載入來自和自身同域(origin)的資料,
現實中,問題是同源策略也並不是萬無一失,跨域攻擊 Cross-site scripting (XSS) 便包含五花八門繞開限制的手段,形式上通過向頁面注入惡意程式碼完成資訊的竊取或攻擊。比如 UGC 型別的站點,因為內容依賴使用者建立,這就開了很大一個口子,允許使用者輸入的內容執行在頁面上。當然,因為我們都知道會有注入攻擊,所以對使用者輸入的內容進行防 XSS 過濾也成了標配。 Content-Security-Policy 從另一方面給瀏覽器加了層防護,能極大地減少這種攻擊的發生。 原理CSP 通過告訴瀏覽器一系列規則,嚴格規定頁面中哪些資源允許有哪些來源, 不在指定範圍內的統統拒絕。相比同源策略,CSP 可以說是很嚴格了。 其實施有兩種途徑:
 為了測試方便,以下示例均使用 一個簡單示例建立一個 HTML 檔案放入以下內容: csp_test.html <!DOCTYPE html>
<html lang="en">
<head>
<meta http-equiv="Content-Security-Policy" content="script-src `self` https://unpkg.com">
<title>CSP Test</title>
</head>
<body>
<script src="https://unpkg.com/react@16/umd/react.development.js"></script>
</body>
</html>
在該測試檔案所在目錄開啟一個本地 server 以訪問,這裡使用 Python 自帶的 server: $ python -m SimpleHTTPServer 8000
然後訪問 localhost:8000 以觀察結果: 然後我們將 csp_test.html <!DOCTYPE html>
<html lang="en">
<head>
- <meta http-equiv="Content-Security-Policy" content="script-src `self` https://unpkg.com">
+ <meta http-equiv="Content-Security-Policy" content="script-src ‘none’>
<title>CSP Test</title>
</head>
<body>
<script src="https://unpkg.com/react@16/umd/react.development.js"></script>
</body>
</html>
下面我們來解釋這裡設定的 CSP 規則及理解為何資源載入失敗。 CSP 規則無論是 header 中還是 示例:
這裡 directive,即指令,是 CSP 規範中規定用以詳細詳述某種資源的來源,比如前面示例中使用的
預設情況下,這些指令都是最大條件開放的,可以理解為其預設值為 還有種特殊的指令 常見的做法會設定
現在來看開頭那個示例,也許現在就能看明白了。因為頁面中需要從 CDN 載入 React 庫,所以我們
這裡的 改成 test.js
同時在頁面中引用它: csp_test.html <!DOCTYPE html>
<html lang="en">
<head>
<meta http-equiv="Content-Security-Policy" content="script-src `none`">
<title>CSP Test</title>
</head>
<body>
<script src="https://unpkg.com/react@16/umd/react.development.js"></script>
+ <script src="./test.js"></script>
</body>
</html>
頁面執行結果: 是的,哪怕是自己的指令碼也無法被載入執行。CSP 就是這樣嚴格和明確,不存在模稜兩可的情況。所以在指定來源時,我們需要確認 URI 是否正確。 指令可接受的值指令後面跟的來源,有兩種寫法
預設值其中預設值有以下這些:
特別地,在 CSP 的嚴格控制下,頁面中內聯指令碼及樣式也會受影響,在沒有明確指定的情況下,其不能被瀏覽器執行。 考慮下面的程式碼: csp_test.html <!DOCTYPE html>
<html lang="en">
<head>
<title>CSP Test</title>
<style>
body{
color:red;
}
</style>
</head>
<body>
<h1>Hello, World!</h1>
<script>
window.onload=function(){
alert(`hi jack!`)
}
</script>
</body>
</html>
根據 MDN 上的描述,如果站點未指定 CSP 無則,瀏覽器預設不會開啟相應檢查,所以上面一切執行正常,只受正常的同域限制 。
我們加上 CSP 限制: csp_test.html <!DOCTYPE html>
<html lang="en">
<head>
+ <meta http-equiv="Content-Security-Policy" content="default-src `self`">
<title>CSP Test</title>
<style>
body{
color:red;
}
</style>
</head>
<body>
<h1>Hello, World!</h1>
<script>
window.onload=function(){
alert(`hi jack!`)
}
</script>
</body>
</html>
配置站點預設只資訊同域的資源,但注意,這個設定並不包含內聯的情況,所以結果會如下圖。 如何修復它呢。如果我們想要允許頁面內的內聯指令碼或樣式,則需要明確地通過 csp_test.html <!DOCTYPE html>
<html lang="en">
<head>
! <meta http-equiv="Content-Security-Policy" content="default-src `self` ‘unsave-inline’”>
<title>CSP Test</title>
<style>
body{
color:red;
}
</style>
</head>
<body>
<h1>Hello, World!</h1>
<script>
window.onload=function(){
alert(`hi jack!`)
}
</script>
</body>
</html>
這裡 重新整理頁面,樣式及指令碼又可以正常執行了。 通常是不建議使用 如果頁面中非得用內聯的寫法,還有種方式。即頁面中這些內聯的指令碼或樣式標籤,賦值一個加密串,這個加密串由伺服器生成,同時這個加密串被新增到頁面的響應頭裡面。 <script nonce=EDNnf03nceIOfn39fn3e9h3sdfa>
// 這裡放置內聯在 HTML 中的程式碼
</script>
頁面 HTTP 響應頭的
注意這裡的 這也就是文章開頭看到的方式,到這裡明白了。
這裡的加密串一定是隨機不可預測的,否則達不到安全效果,且每次頁面被訪問時重新生成。 除了使用 hash 方式的示例: <script>alert(`Hello, world.`);</script>
evaljs 中好些地方是可以以字串方式動態建立程式碼並執行,這被認為是不安全的,所以不推薦使用,一般最佳實踐裡都會提。
和內聯一樣,有專門的指令 setTimout(function(){
alert(1);
}, 1000)
URI除了上面的預設值,還可通過提供完整的 URI 或帶萬用字元
因為 URI 是進行動態匹配的,所以解釋了上面提到的預設值緣何要加引號。因為如果不加引號的話, 優先順序CSP 的配置是很靈活的。每條指令可指定多個來源,空格分開。而一條 CSP 規則可由多條指令組成,指令間用分號隔開。各指令間沒有順序的要求,因為每條指令都是各司其職。甚至一次響應中, 我們來看這些情形下 CSP 的表現。
csp_test.html <!DOCTYPE html>
<html lang="en">
<head>
<meta http-equiv="Content-Security-Policy" content="default-src `self`;default-src `unsafe-inline`;">
<title>CSP Test</title>
<style>
body{
color:red;
}
</style>
</head>
<body>
<h1>Hello, World!</h1>
<script>
window.onload=function(){
alert(`hi jack!`)
}
</script>
</body>
</html>
很智慧地, 瀏覽器不僅會將檢測不過的資源及指令列印出來,重複配置時被忽略的指令也會提示出來。
傳送報告當檢測到非法資源時,除了控制檯看到的報錯資訊,也可以讓瀏覽器將日誌傳送到伺服器以供後續分析使用。接收報告的地址可在
服務端拿到的是以 JSON 形式傳來的資料。 {
"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"
}
}
報告模式CSP 提供了一種報告模式,該模式下資源不會真的被限制載入,只會對檢測到的問題進行上報 ,以 JSON 資料的形式傳送到 通過指定
當然,你也可以同時指定兩種響應頭,各自裡的規則還會正常執行,不會互相影響。比如:
這裡圖片還是會正常載入,但是 報告模式對於測試非常有用。在開啟 CSP 之前肯定需要對整站做全面的測試,將發現的問題及時修復後再真正開啟,比如上面提到的對內聯程式碼的改造。 推薦的做法這樣的安全措施當然是能儘快啟用就儘快。以下是推薦的做法:
瀏覽器相容性目前釋出的 Level 3 規範 中大部分還未被瀏覽器實現,通過 Can I Use 的資料 來看,除 IE 外,Level 2 的功能已經得到了很好的支援。這裡還有一分來自 W3C 跟蹤的各瀏覽器實現情況的統計:Implementation Report for Content Security Policy Level 2。 對於瀏覽器不支援的情況,也不必擔心,會回退到同源策略的限制上。 相關資源 |
Content Security Policy (CSP) 介紹
相關文章
- Content Security Policy
- head頭—內容安全策略(Content Security Policy, CSP)
- Content Security Policy 學習筆記之三:CSP 指令的使用方式筆記
- Content Security Policy 入門教程
- SAP 電商雲 Spartacus UI 裡的 Content Security PolicyUI
- WEB應用內容安全策略(Content Security Policy)Web
- 關於 Web Content-Security-Policy Directive 通過 meta 元素指定的一些測試用例Web
- 關於 Web Content-Security-Policy Directive 透過 meta 元素指定的一些測試用例Web
- Android Content Provider SecurityAndroidIDE
- Spring Security方法級別授權使用介紹Spring
- Spring Security教程 Vol 9. AccessDecisionManager元件介紹Spring元件
- Spring Security教程 Vol 8. AccessDecisionVoter元件介紹Spring元件
- 關於nginx HTTP Strict Transport Security (HSTS) Policy Not Enabled 的處理NginxHTTP
- 課程 3: Content Providers 簡介IDE
- Spring Security中AuthorizationManager簡介Spring
- 介紹
- Filter-Policy過濾策略&Route-policyFilter
- content = content==null? content="":content; 三目運算子用法例項Null
- Laravel Policy 使用Laravel
- LAMP架構介紹、MYSQL介紹、安裝LAMP架構MySql
- php介紹PHP
- CSRedisCore 介紹Redis
- BitMap介紹
- GeoServer介紹Server
- RabbitMQ 介紹MQ
- 模式介紹模式
- Pyzmq介紹MQ
- Java介紹Java
- css介紹CSS
- kafka介紹Kafka
- 【RESTEasy 介紹】REST
- Kafka 介紹Kafka
- PostgreSQLHooK介紹SQLHook
- nginx介紹Nginx
- 埠介紹
- MongoDB介紹MongoDB
- docker 介紹Docker
- TypeScript介紹TypeScript