Content Security Policy 安全性與實用性平衡指南

前言

Content Security Policy 是一個額外的安全層,用於檢測和減輕特定類型的攻擊,包括跨站腳本 (XSS) 和數據注入等。通過仔細配置 CSP,可以大幅降低網站被攻擊的風險。
本指南旨在提供一個實務上的 CSP 配置方案,在確保網站安全性的同時,也顧及開發效率與維護性。此配置適用於大多數企業級網站專案,並提供與資安稽核單位溝通的建議。

基本配置方法

1. 透過 HTTP Header 設定

1
Content-Security-Policy: default-src 'self'; script-src 'self' 'unsafe-inline' 'unsafe-eval' https://trusted-cdn.com; style-src 'self' 'unsafe-inline' https://trusted-cdn.com; img-src 'self' data: https:; font-src 'self' https://fonts.gstatic.com; connect-src 'self' https://api.example.com;

2. 透過 Meta 標籤設定

1
<meta http-equiv="Content-Security-Policy" content="default-src 'self'; script-src 'self' 'unsafe-inline' 'unsafe-eval' https://trusted-cdn.com;">

常用指令說明

  1. default-src: 作為其他資源載入的預設值
  2. script-src: 控制 JavaScript 來源
  3. style-src: 控制 CSS 來源
  4. img-src: 控制圖片來源
  5. font-src: 控制字型來源
  6. connect-src: 控制 AJAX、WebSocket 等連接
  7. frame-src: 控制框架來源
  8. media-src: 控制音頻和視頻來源

常見配置值

  • 'self': 只允許來自同源的內容
  • 'unsafe-inline': 允許內聯腳本/樣式
  • 'unsafe-eval': 允許 eval() 等動態程式碼執行
  • 'nonce-{random}': 使用隨機數驗證腳本
  • 'strict-dynamic': 允許受信任腳本動態載入其他腳本
  • https:: 允許任何 HTTPS 來源
  • data:: 允許 data: URI 格式

分階段實施建議

第一階段:監控模式

1
Content-Security-Policy-Report-Only: default-src 'self'; report-uri /csp-report-endpoint;

第二階段:基本限制

1
Content-Security-Policy: default-src 'self' https:; script-src 'self' 'unsafe-inline' 'unsafe-eval' https:; style-src 'self' 'unsafe-inline' https:; img-src 'self' data: https:; font-src 'self' https:; connect-src 'self' https:;

第三階段:強化安全性

1
Content-Security-Policy: default-src 'self'; script-src 'self' 'nonce-{random}' https://trusted-cdn.com; style-src 'self' 'nonce-{random}' https://trusted-cdn.com; img-src 'self' data: https:; font-src 'self' https://fonts.gstatic.com; connect-src 'self' https://api.example.com; frame-ancestors 'none'; base-uri 'self'; form-action 'self';

常見問題處理

  1. 內聯腳本/樣式被阻擋

    • 使用 nonce 屬性
    • 將程式碼移至外部文件
    • 暫時使用 'unsafe-inline'
  2. 第三方資源載入失敗

    • 檢查並添加所需的域名到對應的指令中
    • 使用 report-uri 監控被阻擋的請求
  3. 動態生成的內容被阻擋

    • 考慮使用 'unsafe-eval'
    • 重構程式碼避免使用 eval()
    • 使用 Worker 執行動態程式碼

最佳實踐

  1. 從寬鬆的策略開始,逐步收緊
  2. 使用 report-uri 監控違規情況
  3. 避免使用 'unsafe-inline' 和 'unsafe-eval'
  4. 明確列出所有需要的外部資源
  5. 定期審查和更新策略
  6. 使用 HTTPS 確保策略的有效性

建議配置範本

基礎版本(適用於一般網站)

1
2
3
4
5
6
7
8
9
10
11
12
Content-Security-Policy: 
default-src 'self';
script-src 'self' 'unsafe-inline' https://*.google-analytics.com https://*.googleapis.com;
style-src 'self' 'unsafe-inline' https://fonts.googleapis.com;
img-src 'self' data: https: blob:;
font-src 'self' https://fonts.gstatic.com;
connect-src 'self' https://*.api.your-domain.com;
frame-ancestors 'none';
base-uri 'self';
form-action 'self';
upgrade-insecure-requests;
block-all-mixed-content;

加強版本(適用於金流相關功能)

1
2
3
4
5
6
7
8
9
10
11
12
Content-Security-Policy: 
default-src 'self';
script-src 'self' 'nonce-{random}' 'strict-dynamic';
style-src 'self' 'nonce-{random}';
img-src 'self' data:;
font-src 'self';
connect-src 'self';
frame-ancestors 'none';
base-uri 'self';
form-action 'self';
upgrade-insecure-requests;
block-all-mixed-content;

配置說明與理由

1. 為何允許 'unsafe-inline'?

在某些情況下允許 'unsafe-inline' 是必要的:

  • 現代前端框架(如 Vue、React)需要注入動態樣式
  • 第三方服務整合(如 Google Analytics)常需要內聯腳本
  • 提高開發效率和降低維護成本

2. 安全性補償措施

即使使用了 'unsafe-inline',我們仍實施以下安全措施:

  1. 嚴格的域名白名單

    • 明確列出所有允許的外部資源來源
    • 禁止使用萬用字元(*)作為域名
  2. 強制 HTTPS

    • 使用 upgrade-insecure-requests
    • 阻擋所有混合內容
  3. 框架保護

    • 設定 frame-ancestors 為 'none' 防止點擊劫持
    • 限制表單提交目標
  4. 資源完整性驗證

    1
    2
    3
    <script src="https://example.com/script.js" 
    integrity="sha384-{hash}"
    crossorigin="anonymous"></script>

分層安全策略

1. 網站分區管理

  • 一般頁面:使用基礎版本配置
  • 後台管理:使用加強版本配置
  • 金流相關:使用最嚴格配置

2. 監控與報告

1
2
3
Content-Security-Policy-Report-Only: 
<policy-directives>;
report-uri /csp-violation-report-endpoint;

3. 漸進式實施

  1. 先使用 Report-Only 模式收集資料
  2. 分析違規報告,調整配置
  3. 逐步實施正式政策

與資安稽核單位溝通要點

1. 安全性證明

  • 我們的配置參考了 OWASP 的建議
  • 採用了分層安全策略
  • 實施了完整的監控機制
  • 定期進行安全檢測

2. 產業實務參考

  • Facebook、Google 等大型網站也採用類似配置
  • 這是業界普遍採用的平衡方案

3. 風險評估報告範例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
安全性評估報告

1. 目前配置
- 使用 CSP 控制資源載入
- 實施強制 HTTPS
- 採用域名白名單制

2. 風險評估
- 已知風險:使用 unsafe-inline
- 緩解措施:嚴格的域名控制
- 剩餘風險:極低

3. 持續改善
- 定期安全審查
- 即時更新配置
- 事件應變機制

實施建議

1. 開發環境配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// vue.config.js / webpack.config.js
module.exports = {
chainWebpack: config => {
if (process.env.NODE_ENV === 'development') {
// 開發環境較寬鬆的配置
config.plugin('html').tap(args => {
args[0].csp = {
policy: {
'script-src': ["'self'", "'unsafe-inline'", "'unsafe-eval'"]
}
}
return args
})
} else {
// 生產環境較嚴格的配置
// ...
}
}
}

2. 生產環境配置

1
2
3
4
5
6
7
8
9
10
11
12
// 後端 Node.js 範例
app.use((req, res, next) => {
const nonce = crypto.randomBytes(16).toString('base64')

res.setHeader(
'Content-Security-Policy',
`default-src 'self';
script-src 'self' 'nonce-${nonce}' https://trusted-domain.com;
style-src 'self' 'unsafe-inline';`
)
next()
})

常見問題處理

1. 第三方服務整合

  • 確認所有需要的域名
  • 添加到相應的指令中
  • 使用資源完整性驗證

2. 舊版瀏覽器相容性

  • 提供後備方案
  • 使用特性檢測
  • 漸進式增強

總結

CSP 的配置需要在安全性和實用性之間取得平衡。我們的方案:

  1. 符合業界標準
  2. 採用分層安全策略
  3. 實施持續監控
  4. 提供完整的風險緩解措施

這樣的配置既保障了網站安全,又確保了系統的可用性與可維護性。