# CSP Header 是必要的嗎?
作為軟體公司的技術管理者,您可能經常遇到資安掃描報告中出現「CSP Header Not Set」的警告。本文將深入探討 Content Security Policy (CSP) 的必要性、實作方式,以及為什麼資安掃描工具會將其視為必要檢查項目。
## CSP 是否為強制性要求?
**答案是:不是強制性的,但強烈建議實施。**
CSP 的實施優先級取決於您的應用場景:
### 高優先級場景
- **面向公眾的網站**:直接暴露於網際網路的應用
- **敏感資料處理**:涉及金融、醫療、個人隱私資料的系統
- **企業級應用**:即使是內部系統,也需要多層防護
- **合規要求**:PCI DSS、GDPR 等法規標準的要求
- **現代化應用**:大量使用 JavaScript 框架的 SPA 應用
### 可彈性處理的場景
- **純靜態展示頁面**:無用戶互動的資訊頁面
- **開發測試環境**:但生產環境仍建議加入
- **遺留系統**:可採用漸進式實施策略
## CSP 的核心價值
Content Security Policy 作為瀏覽器的安全機制,提供以下防護能力:
- **XSS 攻擊防護**:阻止惡意腳本注入和執行
- **資源來源控制**:限制可載入的 JavaScript、CSS、圖片等資源
- **點擊劫持防護**:防止網頁被嵌入惡意框架
- **資料外洩防護**:控制資料傳輸的目標位址
## 實務部署指南
### 基礎設定範例
```http
Content-Security-Policy: default-src 'self';
style-src 'self';
script-src 'self';
img-src 'self' data:;
frame-ancestors 'none';
form-action 'self';
```
## 部署驗證方法
### 1. 瀏覽器開發者工具檢查
1. 開啟目標網站
2. 按 F12 開啟開發者工具
3. 切換至 Network 分頁
4. 重新載入頁面
5. 檢查主要請求的 Response Headers 是否包含 CSP 標頭
### 2. 命令列檢測
```bash
curl -i https://your-domain.com
```
查看回應標頭中的 `Content-Security-Policy` 欄位
### 3. 線上檢測工具
使用 [Security Headers](https://securityheaders.com/) 進行全面的安全標頭檢測
## 資安掃描軟體的判斷邏輯
### 檢測機制
資安掃描工具主要透過 HTTP Response Headers 分析來判斷:
```
檢測邏輯:
IF (HTTP Response 中無 Content-Security-Policy 標頭) THEN
標記為 "CSP Header Not Set"
風險等級 = Medium
建議 = 實施 CSP 政策
END IF
```
### 不同工具的評估標準
| 掃描工具 | 檢測範圍 | 風險等級 | 主要考量 |
|---------|---------|---------|---------|
| OWASP ZAP | 所有 Web 回應 | Medium | OWASP Top 10 合規 |
| Nessus | 動態內容頁面 | Medium | 企業安全標準 |
| Burp Suite | JavaScript 執行頁面 | Medium-High | 實際攻擊向量 |
| Security Headers | 所有 HTTP 回應 | Warning | 最佳實務建議 |
### 為什麼採用統一標準?
1. **預防性安全原則**:假設所有 Web 應用都存在潛在風險
2. **自動化檢測需求**:無法動態判斷應用的複雜度和風險等級
3. **標準化合規要求**:遵循 OWASP、NIST 等安全框架
4. **成本效益考量**:統一實施比個案評估更具經濟效益
## 分階段實施策略
### 階段一:觀察模式 (Report-Only)
```http
Content-Security-Policy-Report-Only: default-src 'self'
```
- 收集違規報告
- 分析現有應用的資源載入模式
- 評估實施影響
### 階段二:政策調整
- 根據報告調整 CSP 規則
- 處理第三方服務整合
- 測試關鍵功能完整性
### 階段三:正式部署
```http
Content-Security-Policy: default-src 'self'; script-src 'self'
```
- 移除 Report-Only 模式
- 監控應用運行狀況
- 建立長期維護機制
## 技術決策建議
### 風險評估矩陣
**高風險應用(立即實施)**
- 處理用戶敏感資料
- 面向公眾的商業應用
- 金融、醫療相關系統
**中風險應用(計劃實施)**
- 企業內部系統
- 有限用戶群的應用
- 資料敏感度中等的系統
**低風險應用(可延後實施)**
- 純展示型網站
- 測試開發環境
- 無用戶互動的靜態頁面
### 實施成本考量
**開發成本**
- CSP 政策設計:2-4 MD
- 測試和調整:4-8 MD
- 文件和維護:1-2 MD
**維護成本**
- 定期政策檢視
- 第三方服務整合調整
- 安全事件回應機制
**綜合常見修改工作量評估**
- **小型專案**:2-4 MD
- **中型專案**:4-8 MD
- **大型專案**:14-28 MD
**耗時最多時間花在**
1. 找出所有內聯樣式和事件
2. 測試第三方函式庫相容性
3. 調整動態樣式邏輯
## 結論與建議
CSP Header 雖非法律強制要求,但在現今的網路威脅環境下,它是一個**投資回報率極高的安全措施**。資安掃描軟體將其標記為必要檢查項目,反映了業界對於「縱深防禦」安全理念的共識。
**建議行動方案:**
1. **立即行動**:為面向公眾的生產環境實施基礎 CSP
2. **漸進部署**:採用 Report-Only 模式進行測試和調整
3. **持續優化**:建立 CSP 政策的定期檢視和更新機制
4. **團隊培訓**:確保開發團隊理解 CSP 的原理和最佳實務
記住,安全不是一次性的實施,而是持續的過程。CSP 作為現代 Web 安全架構的重要組成部分,值得每個技術團隊認真對待和實施。
## ZAP 與 Angular 程式碼實務
以下為實際通過ZAP 運作的 CSP header 內容, 注意到它未使用了 unsafe-inline, 增加了 fallback 處理(frame-ancestors & form-action)。
```
default-src 'self'; style-src 'self'; script-src 'self'; img-src 'self' data:; frame-ancestors 'none'; form-action 'self';
```
要讓 Angular 應用能使用最嚴格的 CSP (`'self'` only),需要進行以下修改:
## 1. Angular 建置配置修改
### 修改 `angular.json`
```json
{
"projects": {
"your-app": {
"architect": {
"build": {
"options": {
"extractCss": true,
"optimization": true,
"buildOptimizer": true,
"aot": true,
"vendorChunk": true,
"commonChunk": true,
"namedChunks": false
},
"configurations": {
"production": {
"extractCss": true,
"optimization": {
"scripts": true,
"styles": {
"minify": true,
"inlineCritical": false
}
}
}
}
}
}
}
}
}
```
關鍵設定:
- `"extractCss": true` - 將所有樣式提取到外部檔案
- `"inlineCritical": false` - 禁用關鍵 CSS 內聯
## 2. 移除內聯事件處理器
### 修改 HTML 模板
**❌ 避免使用:**
```html
<button onclick="doSomething()">按鈕</button>
<div onmouseover="showTooltip()">懸停</div>
```
**✅ 改為:**
```html
<button (click)="doSomething()">按鈕</button>
<div (mouseover)="showTooltip()">懸停</div>
```
## 3. 處理動態樣式
### 使用 Angular Renderer2
**❌ 避免直接操作 style:**
```typescript
// 不要這樣做
element.style.color = 'red';
element.setAttribute('style', 'color: red;');
```
**✅ 使用 Renderer2:**
```typescript
import { Renderer2, ElementRef } from '@angular/core';
constructor(private renderer: Renderer2, private el: ElementRef) {}
// 設定樣式
this.renderer.setStyle(this.el.nativeElement, 'color', 'red');
// 或使用 CSS 類別
this.renderer.addClass(this.el.nativeElement, 'red-text');
```
### 使用 CSS 類別替代內聯樣式
**❌ 避免:**
```typescript
@Component({
template: `<div [style.color]="dynamicColor">文字</div>`
})
```
**✅ 改為:**
```typescript
@Component({
template: `<div [class.red-text]="isRed" [class.blue-text]="isBlue">文字</div>`,
styles: [`
.red-text { color: red; }
.blue-text { color: blue; }
`]
})
```
## 4. 第三方函式庫處理
### 檢查並替換有問題的函式庫
```bash
# 檢查哪些函式庫使用內聯樣式
npm audit --audit-level moderate
```
### 常見問題函式庫替代方案
- **Bootstrap**: 使用 `ng-bootstrap` 而非原生 Bootstrap
- **jQuery**: 盡量用 Angular 原生方法替代
- **圖表函式庫**: 選擇支援 CSP 的版本(如 Chart.js 配置 CSP 模式)
## 5. 測試配置
### 本地測試 CSP
在 `src/index.html` 中暫時加入:
```html
<meta http-equiv="Content-Security-Policy"
content="default-src 'self'; style-src 'self'; script-src 'self'; img-src 'self' data:;">
```
## 6. 建置和部署
```bash
# 使用 production 建置
ng build --configuration=production
# 檢查生成的檔案中是否有內聯內容
grep -r "style=" dist/
grep -r "onclick=" dist/
```
## 7. 段階式驗證
### 階段 1:先測試樣式
```
Content-Security-Policy: default-src 'self'; style-src 'self'; script-src 'self' 'unsafe-inline'; img-src 'self' data:;
```
### 階段 2:再測試腳本
```
Content-Security-Policy: default-src 'self'; style-src 'self'; script-src 'self'; img-src 'self' data:;
```
## 參考網站
- https://developer.mozilla.org/en-US/docs/Web/HTTP/Guides/CSP