跳轉到

6.2 安全開發生命週期與 Code Review#

把安全「左移」:在設計與開發階段解決#

6.1 的滲透測試是事後驗證。SDL(Security Development Lifecycle,微軟 2004 年因 Blaster 蠕蟲停機一個月而制度化)的洞見:

\[ \text{修復成本隨階段指數上升}: \text{設計} \ll \text{開發} \ll \text{上線後} \]

(本系列全書的反覆主題:「把錯誤往時間軸左端移」—— 型別系統、SQLx 編譯期檢查、以及現在的 SDL。)

SDL 全景#

每個階段的安全活動:

階段 活動 本書工具
需求 個資盤點(5.2)、合規需求 檢查清單
設計 DFD + STRIDE、資料分級 1.2 四問法
開發 編碼規範、code review、SAST 本章
測試 單元測試含安全案例、DAST、滲透 6.1
部署 依賴掃描(4.2)、配置基線(2.4) CI 檢查
營運 告警(5.3)、事件回應(5.4) 手冊

安全的 Code Review#

普通 review 問「對不對」,安全 review 額外問「被濫用會怎樣」。檢查清單(按風險排序):

新端點檢查#

[ ] 輸入:提取器/驗證覆蓋所有參數?(前書 3.2, 3.3 白名單)
[ ] 認證:在保護組內?預設拒絕?(3.3)
[ ] 授權:owner 條件?角色檢查?(3.3 IDOR)
[ ] 限流:昂貴操作有限制?(3.4)
[ ] 輸出:無洩漏內部細節?(前書 2.4 錯誤處理)
[ ] 日誌:安全事件有記?無敏感明文?(5.3)

高危代碼模式(review 時全文搜尋)#

# 危險訊號搜索清單
grep -rn "innerHTML\|dangerouslySet" src/        # XSS(2.1)
grep -rn "exec\|eval\|system(" src/              # 命令注入(3.1)
grep -rn "query(.*+\|f\"" src/                   # 字串拼接 SQL(3.1)
grep -rn "redirect(.*req\." src/                 # 開放重導向(2.3)
grep -rn "process.env" src/                      # 秘鑰使用點盤點(4.3)

工具化這些檢查(lint 規則、grep 進 CI)比「靠 reviewer 記住」可靠 —— 人會累,regex 不會。

自動化安全測試:SAST 與 DAST#

類型 時機 原理 工具
SAST 靜態 寫碼時/CI 分析源碼找危險模式 Semgrep、CodeQL、cargo clippy+lint
DAST 動態 測試環境 黑盒打真實應用 OWASP ZAP、nuclei
SCA 依賴 CI 對照 CVE 資料庫 4.2 audit

Semgrep:可自訂的結構化搜索#

# 危險的 innerHTML 賦值
rules:
  - id: unsafe-innerhtml
    patterns:
      - pattern: $EL.innerHTML = $X
    message: "innerHTML 可致 XSS,使用 textContent 或 DOMPurify"
    severity: ERROR

SAST 的天花板:誤報與「不懂業務邏輯」。實務配置為增量模式(只掃 PR 的 diff),把噪音壓到可執行。

威脅建模的輕量化實踐#

大型正式建模(1.2 四問法)不必每次全跑。輕量儀式:新功能 PR 描述中必填三行:

## 安全考量
- 輸入:接受哪些不可信資料?如何驗證?
- 授權:誰可以呼叫?如何確認?
- 資料:接觸哪些個資?記了什麼日誌?

讓安全思考成為流程的預設欄位 —— 對抗遺忘的不是記憶力,是表格(checklist 的社會工程)。

安全文化:最後的防線是人#

反模式 症狀 解方
安全=資安團隊的事 開發者不學不問 每人背基本清單(6.3 實戰)
安全=延期藉口 「先上線再說」 增量修復承諾+自動化降低成本
責怪報告者 漏洞被隱瞞 blameless(5.4)+ 獎勵發現
一次性的安全專案 補完就爛 持續的 CI 檢查+定期演練
\[ \text{安全的複利}: \text{每個 PR 的小檢查} \xrightarrow{\text{數年}} \text{整體安全水位} \]

習題#

  1. 把「高危代碼模式搜索」寫進 CI(Semgrep 或 grep 腳本),對現有專案跑第一次清掃。
  2. 為你的下一個功能 PR 建立安全三行範本,並在團隊試行一週記錄發現的問題。
  3. 比較 SAST 與 DAST 各自抓不到的漏洞類型,說明為何兩者都要。