
安全開發生命週期 (SDL):把安全內建到開發流程
安全不是最後一關的審查,而是每一步都在做的事。
「事後審查」模式的問題
傳統的安全流程:
需求 → 設計 → 開發 → 測試 → 部署 → 安全審查
↑
「你這裡有漏洞」
「要改架構」
「上線延後」
問題在於:
- 發現問題時已經太晚,修復成本極高
- 安全團隊變成「壞人」,總是在最後一刻擋住上線
- 開發者不知道為什麼,覺得安全是阻礙而不是幫助
- 只能找到表面問題,設計層面的安全缺陷已經無法修正
Shift Left — 把安全往左移
需求 設計 開發 測試 部署 運維
↑ ↑ ↑ ↑ ↑ ↑
威脅 安全 安全 安全 安全 安全
模型 設計 編碼 測試 部署 監控
Review SAST Config Logging
DAST Review Alerting
核心觀念:在每個階段都融入安全考量,而不是集中在最後。 越早發現問題,修復成本越低。
SDL 的六個階段
1. 需求階段:威脅模型
在寫任何 code 之前,先問:「這個功能可以被怎麼濫用?」
最小可行的威脅模型(10 分鐘版):
| 問題 | 你的回答 |
|---|---|
| 這個功能處理什麼敏感資料? | (例:使用者的 email、信用卡號) |
| 誰會用這個功能? | (例:已登入的使用者、管理員) |
| 如果這個功能被濫用,最壞會怎樣? | (例:使用者資料外洩、金額被竄改) |
| 攻擊者最可能從哪裡下手? | (例:API 參數竄改、CSRF) |
| 我們需要什麼防禦? | (例:input validation、權限檢查、CSRF token) |
完整的威脅模型(用於重要功能):
- 畫出資料流圖(DFD)
- 在每個信任邊界上套用 STRIDE
- 為每個威脅評估風險(可能性 × 影響)
- 決定應對策略:避免、緩解、轉移、接受
2. 設計階段:安全設計原則
| 原則 | 意思 | 例子 |
|---|---|---|
| 最小權限 | 只給剛好需要的權限 | DB 帳號只給 SELECT,不給 DROP |
| 縱深防禦 | 不依賴單一安全措施 | WAF + Input Validation + Parameterized Query |
| 預設安全 | 預設狀態是安全的 | 新建的 API endpoint 預設需要認證 |
| 失敗安全 | 出錯時回到安全狀態 | Token 驗證失敗 → 拒絕存取(不是允許) |
| 簡單原則 | 越簡單越不容易出錯 | 用成熟的 library 而不是自己實作加密 |
「預設安全」特別重要: 如果你的框架需要開發者「記得加上」安全措施才安全,那一定會有人忘記。好的設計是:如果你什麼都不做,它就是安全的。
3. 開發階段:安全編碼
建立 coding guideline 中的安全規範:
## 安全編碼規範
### Input Validation
- 所有外部輸入都必須驗證(type, length, range, format)
- 使用白名單(允許什麼)而非黑名單(禁止什麼)
### 資料庫
- 一律使用 Parameterized Query
- 禁止用字串拼接 SQL
### 認證與授權
- 密碼一律用 bcrypt(cost factor ≥ 12)
- Session token 使用 crypto-random
- 每個 API endpoint 都要檢查權限
### 敏感資料
- 禁止在 log 中輸出密碼、token、信用卡號
- .env 和 secrets 不入版控
- 使用環境變數或 secret managerCode Review 時的安全 checklist:
- 有沒有使用者輸入直接進了 SQL / HTML / Shell?
- 新的 API endpoint 有檢查權限嗎?
- 有沒有硬編碼的 secret?
- 錯誤訊息有沒有洩漏內部資訊?
- 有沒有新的 dependency?有已知漏洞嗎?
4. 測試階段:安全測試工具
| 工具類型 | 全稱 | 做什麼 | 什麼時候用 |
|---|---|---|---|
| SAST | Static Application Security Testing | 掃描原始碼找漏洞 | CI pipeline 中,每次 commit |
| DAST | Dynamic Application Security Testing | 對運行中的應用發送攻擊請求 | 部署到 staging 後 |
| SCA | Software Composition Analysis | 檢查第三方套件的已知漏洞 | CI pipeline 中,每次 commit |
| Secret Scanning | — | 找 code 中的硬編碼 secret | pre-commit hook |
常用工具:
# SAST
- Semgrep # 開源,支援多語言,規則可自訂
- CodeQL # GitHub 內建,免費
# SCA (dependency audit)
- npm audit # Node.js 內建
- Snyk # 多語言,有免費版
- Dependabot # GitHub 內建
# Secret Scanning
- gitleaks # pre-commit hook
- GitHub Secret Scanning # 免費
# DAST
- OWASP ZAP # 開源,功能完整
- Burp Suite # 業界標準(付費版功能更多)務實建議: 不需要全部都用。從這個組合開始:
- Semgrep in CI(SAST)
- npm audit in CI(SCA)
- gitleaks as pre-commit hook(Secret Scanning)
這三個都是免費的,10 分鐘就能設好。
5. 部署階段:安全設定
# 部署 checklist
- [ ] Debug mode 關閉
- [ ] 預設帳號密碼已更改
- [ ] HTTPS 強制開啟
- [ ] Security headers 已設定
- [ ] 不需要的 port 已關閉
- [ ] 最小權限的 service account
- [ ] Container 以 non-root 執行
- [ ] Secret 從 secret manager 讀取,不從環境變數Infrastructure as Code 的安全掃描:
tfsec— Terraformcheckov— 多種 IaC 格式kube-bench— Kubernetes
6. 運維階段:持續監控
該監控的安全指標:
| 指標 | 為什麼重要 | 告警閾值建議 |
|---|---|---|
| 登入失敗次數 | 暴力破解偵測 | 同一 IP 5 分鐘內 > 10 次 |
| 權限錯誤 (403) | 權限繞過嘗試 | 同一使用者 1 分鐘內 > 5 次 |
| 異常的 API 呼叫量 | DDoS 或 data scraping | 超過 baseline 3 倍 |
| 新的 admin 帳號建立 | 可能被提權 | 任何非預期的建立 |
| 大量資料匯出 | Data exfiltration | 超過正常量的 10 倍 |
DevSecOps — 把 SDL 自動化
開發者 push code
│
├── pre-commit: gitleaks(掃 secret)
│
├── CI pipeline:
│ ├── Semgrep(SAST)
│ ├── npm audit(SCA)
│ └── Unit tests with security cases
│
├── Deploy to staging:
│ └── OWASP ZAP scan(DAST)
│
└── Production:
├── Security headers check
├── Runtime monitoring
└── Alerting
核心思維:安全檢查應該是自動的、快速的、不阻礙開發流程的。 如果安全檢查讓 CI 多跑 30 分鐘,開發者會想辦法繞過它。
一個人怎麼做 SDL
如果你是個人開發者或小團隊,完整的 SDL 太重了。最小可行的安全流程:
- 寫新功能前 — 花 5 分鐘回答:「這個功能可以被怎麼濫用?」
- 寫 code 時 — 記住三條:input validation、parameterized query、最小權限
- commit 前 — 跑
gitleaks確保沒有 secret - CI 裡 — 加
npm audit和semgrep - 部署後 — 用 securityheaders.com 掃一次
總共增加的時間:每天 15 分鐘。換來的是大幅降低被攻擊的風險。
反思問題
- 你的 CI pipeline 裡有安全掃描嗎? 如果沒有,今天就加一個
npm audit。 - 你的 .git 歷史裡有 secret 嗎? 很多人忘記 git 有歷史記錄——即使你在新 commit 裡刪除了 .env,舊 commit 裡還在。
- 你的團隊在 code review 時會看安全嗎? 還是只看功能對不對?
- 如果明天你的系統被爆出漏洞,你有應變計畫嗎? 搭配 事故管理 來準備。
延伸閱讀
- 資安基礎概念 — 安全思維的基礎
- 紅隊與藍隊 — 攻防視角
- Web 應用安全實務 — 具體的漏洞與防禦
- 測試策略 — 測試金字塔中安全測試的定位
- Code Review 方法論 — Review 時的安全 checklist