iT邦幫忙

2026 iThome 鐵人賽

DAY 2
0

功能需求寫「應該發生什麼」,安全需求寫「不能發生什麼」。後者 AI 不會主動替你寫。

大綱

  • 情境:「幫我加一個分享清單給朋友的功能」
  • 功能需求與安全需求:一個說「要」,一個說「不能」
  • Abuse case:把每個功能反過來問一次
  • 五個思考模型:每個階段都要問的五個問題
  • 30 秒自我檢查
  • 提示詞:在 AI 動手之前先交出 threat model
  • 機制:安全需求是「負面需求」,很難從提示詞推導
  • Data flow diagram 與 trust boundary
  • STRIDE:六類威脅,對應這個系列的篇章
  • 把威脅寫成可以測試的安全需求
  • 讓 threat model 跟著專案活下去
  • 對應標準:Insecure Design、OWASP Threat Modeling、NIST SSDF、OWASP ASVS
  • 攔截點:該在哪個 SSDLC 階段攔下、要問的問題
  • 劃重點

【情境】

小安(第 1 天情境裡的那位)想替他的預約網站加一個功能。他跟 AI 說:

幫我加一個「把預約分享給朋友」的功能,朋友點連結就能看到預約時間。

十分鐘後功能做好了,測試也通過:點分享、複製連結、朋友打開、看得到時間。

他沒有問、AI 也沒有提的問題是:

  • 分享連結可以被猜出來嗎?
  • 朋友看得到的,只有時間,還是連電話、備註都看得到?
  • 取消分享之後,舊連結還能用嗎?
  • 有人把連結貼到公開社團,會怎樣?

這些問題的答案,決定了這個功能是「分享」還是「外洩」。

功能需求與安全需求

面向 功能需求 安全需求
句型(以分享為例) 使用者可以分享預約 沒拿到分享連結的人不能看到預約
誰會提出來 你會說,AI 會做 你通常不會說,AI 有時會補、有時不會
怎麼測 做一次,成功就好 要把所有「不該成功」的方法試過一輪
什麼時候發現缺了 開發當下 通常是上線之後

在 SSDLC 裡,需求與設計階段的工作,就是把安全需求在寫程式之前寫出來。因為改一份規格只要五分鐘,改一個已經上線、有兩百個使用者的服務,可能要一個週末加一封道歉信。

Abuse case:把每個功能反過來問

一般的使用情境(use case)問「使用者想做什麼」。Abuse case 問的是:「一個不懷好意的人,會怎麼用這個功能?」

功能 正常用法 濫用方式
分享預約 傳給朋友看時間 改連結裡的編號,看到別人的預約
手機簡訊驗證 註冊時收驗證碼 一直按「寄送」,讓你的簡訊費暴增(第 27 天)
AI 幫我寫文案 一天用幾次 寫程式一直按,把你的服務當成免費的 AI(第 27 天)
優惠碼 輸入一次折 100 元 同時送出 20 次,折 20 次
上傳大頭貼 上傳一張照片 上傳一個 2GB 的檔案,或一個偽裝成圖片的網頁

把右邊那一欄反過來寫成「不能……」的句子,就是你要交給 AI 的安全需求。

五個思考模型:每個階段都要問的五個問題

這個系列用 SSDLC 決定什麼時候做,用五個思考模型決定做的時候問什麼。它們不是只在今天用,接下來 28 篇的每一篇,都會標出它主要用到哪一個。

思考模型 要問的問題 用在分享功能上
信任邊界 這份資料從哪裡來?誰能控制它? 分享連結裡的編號,使用者改得動嗎?
身分與權限 確認了是誰之後,有沒有確認他能做這件事? 拿到連結就能看,還是要登入並確認是被分享的人?
資料被當成程式執行 這段內容,會不會被某個東西執行? 預約備註會顯示在朋友的畫面上,如果備註裡是一段 <script> 呢?
狀態與時間 過了一段時間、或同時發生很多次,還成立嗎? 取消分享之後,舊連結還有效嗎?
預設值與依賴 我沒特別設定的東西,現在是什麼狀態? 新增的 shares 資料表,預設誰都讀得到嗎?

五個問題問完,通常就能列出一半以上的 abuse case。剩下的,交給下面的 STRIDE。

30 秒自我檢查

挑你服務裡最重要的一個功能,回答:

  1. 這個功能不能讓誰做什麼?寫得出至少三句嗎?
  2. 這個功能被一個人連續用一萬次,會怎樣?
  3. 你寫下的這些「不能」,AI 知道嗎?還是只在你心裡?

貼給 AI 的提示詞

這段提示詞放在請 AI 實作新功能之前。重點是最後一句:AI 預設會直接開工,要讓它先交設計。

在開始實作之前,請先輸出這個功能的 threat model,不要寫任何程式碼:

1. 資料流:使用者 → 前端 → API → 資料庫/第三方服務,標出每一個 trust boundary
2. 針對每一個 trust boundary,用下面五個問題列出 abuse case:
   - 這份資料從哪裡來?誰能控制它?
   - 確認了是誰之後,有沒有確認他能做這件事?
   - 這段內容,會不會被某個東西執行(資料庫、瀏覽器、AI)?
   - 過了一段時間、或同時發生很多次,還成立嗎?
   - 沒有特別設定的東西(資料表權限、預設值、套件),現在是什麼狀態?
3. 每一個 abuse case:對應的防護措施,以及一個「證明防護有效」的測試案例(寫成「用某身分做某事,應該被拒絕」)

列完後停下來,等我確認再開始實作。

機制:安全需求是「負面需求」

功能需求描述「應該發生什麼」,可以直接變成程式碼和測試。安全需求大多描述「不能發生什麼」,它的測試空間是無限的:不能越權、不能注入、不能 replay、不能被濫用。

LLM 生成程式碼時,主要依據提示詞裡明說的目標,再加上訓練資料裡常見的寫法。我的觀察是:教學範例、quick start 文件為了簡潔,最常省略的正是 authorization、rate limit、錯誤處理,所以 AI 產出的程式碼,常常像一份很完整的教學範例,離能上線的服務還差一段。

研究也看到類似的結果。2022 年發表在 IEEE S&P 的研究,讓 GitHub Copilot 在 89 個跟 CWE Top 25 相關的情境裡補完程式碼,產出的 1,689 個程式約 40% 有漏洞。Veracode 在 2025 年 7 月測了 100 多個 LLM,45% 的程式碼樣本沒通過安全測試,而且模型越大、越新,安全表現也沒有明顯變好。

工程師用 AI 時也有同樣的問題:你心裡知道要檢查權限,但提示詞裡沒寫;review 時又因為產出量太大,沒有逐行看。Stanford 的使用者研究(CCS 2023)發現,能用 AI 助手的受試者寫出的程式碼比較不安全,卻更相信自己寫的是安全的。threat model 的價值,是把「心裡知道」變成「寫在紙上、AI 讀得到、測試對得到」。

Data flow diagram 與 trust boundary

Threat modeling 的第一步是畫資料流圖(data flow diagram,DFD)。不需要工具,紙筆就可以。以小安的分享功能為例:

https://ithelp.ithome.com.tw/upload/images/20260916/201030683EwMPEeNre.png

圖上虛線框起來的 trust boundary,就是第一個思考模型「信任邊界」畫在圖上的樣子。

每一條穿過 trust boundary 的箭頭,都是一個要問問題的地方。瀏覽器在邊界外,所以從瀏覽器進來的每一個參數,都是不可信的;簡訊服務在邊界外,所以每一次呼叫都是一筆費用。

STRIDE:六類威脅

STRIDE 是 Microsoft 的 Loren Kohnfelder 和 Praerit Garg 在 1999 年提出的威脅分類,拿來檢查「還有沒有漏掉的類型」很好用。以這個系列的靶場「會員制待辦清單」為例(包含第 3 週會加上的重設密碼和管理後台):

威脅 意思 待辦清單裡的例子 對應篇章
Spoofing 冒充別人 重設密碼流程被利用,登入別人的帳號 第 14 天
Tampering 竄改不該改的資料 改網址 id,修改別人的待辦 第 4、19 天
Repudiation 做了壞事卻查不到 沒有 audit log,不知道誰刪了清單 第 29 天
Information disclosure 看到不該看的 資料庫沒設權限,清單全部外洩 第 5 天
Denial of service 讓服務不能用或變很貴 簡訊驗證、AI 按鈕被灌爆 第 27 天
Elevation of privilege 變成更高權限的角色 更新個人資料時順便把自己改成管理員 第 13 天

做法:在 DFD 上每一條穿過 trust boundary 的箭頭,把六個字母各問一次。五個思考模型負責「換個角度想」,STRIDE 負責「確認沒有漏掉整個類別」。

把威脅寫成可以測試的安全需求

「要注意權限」不是需求,它沒辦法測。一條好的安全需求要能直接變成測試:

模糊的寫法 可測試的寫法
分享功能要安全 以隨便編造的 token 呼叫 GET /share/:token,回應 404;以有效 token 讀取,回應只有預約時間,不含電話與備註
要防止簡訊濫用 同一個手機號碼 60 秒內第二次請求驗證碼,回應 429,且簡訊服務沒有被呼叫
取消分享要有效 擁有者取消分享後,以舊 token 讀取,回應 404
要防止權限提升 一般會員以 PATCH /profile 送出 role=admin,資料庫中的 role 不變

右邊的句子,第 4 天會變成權限表,第 19 天會變成 regression test,第 22 天會變成 AI 審查時的「答案卷」。

自己想不到要寫哪些句子時,可以拿 OWASP ASVS(Application Security Verification Standard)當清單。
5.0 版在 2025 年 5 月發布,大約 350 條需求,分成 L1 到 L3 三個等級。整份對小型服務太多,下面挑五條跟這篇例子直接相關的,示範怎麼翻成你自己服務的句子:

ASVS 5.0 條目(等級) 條文重點 寫成小安服務的安全需求
8.2.2(L1) 資料層級的存取,只開放給對那筆資料有明確權限的人,防止 IDOR 已登入的使用者 A 以 GET /bookings/:id 讀取使用者 B 的預約,回應 404
15.3.1(L1) API 只回傳需要的欄位,不回傳整個資料物件 GET /share/:token 的回應只有 start_timeend_time 兩個欄位
15.3.3(L2) 防止 mass assignment:每個動作只允許寫入它該寫的欄位 PATCH /profile 送出 role=adminis_verified=true,資料庫中的值不變
2.4.1(L2) 有 anti-automation 控制,防止功能被大量呼叫、耗盡額度或花掉昂貴資源 同一支手機一天內第 6 次請求驗證碼,回應 429,簡訊服務沒有被呼叫
2.3.4(L2) 數量有限的資源(例如座位、時段)要有鎖定機制,不能被重複預約 兩個請求同時預約同一個時段,只有一個成功,資料庫裡只有一筆預約

簡訊的上限跟第 27 天的建議一致(每分鐘最多 1 封、每天最多 5 封),實際數字要依你的服務調整。

讓 threat model 跟著專案活下去

Threat model 不是寫一次就結束的文件。實務上:

  • 存成 repo 裡的 docs/threat-model.md,跟程式碼一起進版控
  • 每次請 AI 做跨越 trust boundary 的新功能(新 API、新第三方服務、新的使用者上傳內容、新的 AI 功能),先更新它
  • PR 描述裡附上「這次變更影響了 threat model 的哪一條」

注意它的性質:threat model 是規格,讓 AI 和人知道要做什麼、測什麼。它不是安全邊界,真正擋住攻擊的是程式、權限設定和測試(第 8 天會談「告示」和「鎖」的差別)。

對應標準

  • OWASP Top 10:2025:A06 Insecure Design(2021 版是 A04)。它的防範建議之一,就是對驗證、存取控制、商業邏輯這些關鍵流程做 threat modeling。
  • OWASP Threat Modeling Cheat Sheet、Threat Modeling Manifesto
  • STRIDE(Loren Kohnfelder、Praerit Garg,Microsoft,1999)
  • NIST SP 800-218(SSDF 1.1):PO.1 Define Security Requirements for Software Development;PW.1 Design Software to Meet Security Requirements and Mitigate Security Risks,其中 PW.1.1 要求用 threat modeling 這類 risk modeling 評估軟體的安全風險,PW.1.2 要求持續追蹤與維護安全需求、風險和設計決策(對應「讓 threat model 跟著專案活下去」)
  • OWASP ASVS 5.0.0(2025 年 5 月):V2 Validation and Business Logic、V8 Authorization、V15 Secure Coding and Architecture

【攔截點】

  • 最早該在這裡攔下:需求與設計。在請 AI 實作之前,先交出 threat model,把「不能發生的事」寫成可以測試的句子。
  • 漏掉時的下一道網:需求與設計(第 4 天權限表);驗證(第 19 天兩個帳號測試、把安全需求寫成 regression test;第 22 天請 AI 對著安全需求審查)。
  • 要問的問題(思考模型:全部):這個功能不能讓誰做什麼?這些句子寫在 AI 讀得到的地方了嗎?

【劃重點】

AI 會完成你說的功能,但不會替你想「壞人會怎麼用它」。在它動手之前,用五個問題把「不能發生的事」寫下來,寫成可以測試的句子。

參考資料


上一篇
D01 AI 幫你寫出服務,但沒告訴你的事
下一篇
D03 瀏覽器看得到的,全都是公開的
系列文
AI 寫的程式,安全嗎?完工不是結束,是攻擊倒數的起點5
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言