資訊處理安全確保支援士(情報処理安全確保支援士) 令和6年度春季 下午問1 解說 ── JWT 的 alg=none 與 API 授權、WAF 的暫定對策
· 更新日期: · Go Komura · 資訊處理安全確保支援士, 登錄資訊處理安全確保支援士, API, API安全, JWT, 認證, 授權, WAF, Log4Shell, 資訊安全, 漏洞, IPA
更新紀錄(1 筆,最後更新 2026年09月13日)
本文的修改紀錄。已保存的更新前版本,可透過附有 DOI 的永久連結閱讀。
- 移除了殘留在文章原始檔末尾的一行多餘結尾標籤(</content>)。渲染器原本就會忽略它,頁面上沒有任何變化。
- 初次發布
引用本文(DOI(已登錄存檔): 10.5281/zenodo.22176023)
以下 DOI 指向先前登錄的存檔,內容可能與目前正文不同。引用目前正文時,請使用本頁網址。
Go Komura(2026)。〈資訊處理安全確保支援士(情報処理安全確保支援士) 令和6年度春季 下午問1 解說 ── JWT 的 alg=none 與 API 授權、WAF 的暫定對策〉。小村軟體有限公司。 https://comcomponent.com/zh-TW/blog/sc-exam-r6s-pm-q1-api-security/
- DOI(已登錄存檔)
- 10.5281/zenodo.22176023
- DOI(上次登錄版本)
- 10.5281/zenodo.22176024
「明明在驗證 JWT,卻還是能冒充他人」「JWT 保持正確,卻能讀到別人的資料」。這兩件事看起來相似,但要修的地方不同。前者是權杖驗證的問題,後者是對目標資料的授權問題。
資訊處理安全確保支援士考試(情報処理安全確保支援士試験)令和6年度春季下午問1,以智慧型手機呼叫的 API 為題材,考的是如何讀懂這些確認之間的差別。前半部處理 JWT、使用者 ID、更新欄位與認證碼,後半部處理函式庫的漏洞與 WAF。1
本文以官方的解答範例與評分講評為基礎,依 「解答要點 → 題目原文中的依據 → 實務上要追加的設計」 的順序說明。閱讀時請把考試字數限制內要寫的內容,和實際系統所需的對策分開來看。23
1. 先講結論 ── 就算驗證通過,也不能省略下一道確認
貫穿本文的原則是:不要把上一道驗證通過,當成省略下一道信任邊界的理由。所謂信任邊界,是用來決定外部傳來的值在什麼條件下才可以信任的分界。
持有正確的 JWT,不代表就可以讀取他人的資料。能夠更新自己的資料,也不代表連付費狀態都可以變更。認證碼的有效期限與 WAF 同樣各自需要不同的確認與維運方式。
解答全貌
下表是官方解答的要點。文字型的解答請到各章確認設問的字數限制與依據。2
| 設問・空格 | 解答要點 | 詳細說明 |
|---|---|---|
| 設問1・a | 無狀態(stateless) | 第 3 章 |
| 設問2(1)・b | 500 秒 | 第 4 章 |
| 設問2(2) | 以 JWT 標頭的 alg 為對象,驗證其值不是 NONE | 第 5 章 |
| 設問2(3) | 驗證 JWT 的使用者 ID 與 mid 是否一致 | 第 6 章 |
| 設問2(4)・c | 共用模組 P | 第 7 章 |
| 設問2(5)・d | 連續失敗次數超過門檻就鎖定帳戶 | 第 8 章 |
| 設問3(1) | 記錄並確認對測試伺服器 index.html 的存取 | 第 9 章 |
| 設問3(2)・e、f | 兩者都是 Header | 10.1 節 |
| 設問3(3) | 同時對應大寫與小寫的正規表示式 | 10.2 節 |
| 設問3(4) | 避免誤判造成的阻斷,並詳查警示 | 10.3 節 |
先在第 2 章掌握題目的結構,再依設問順序讀第 3 到第 10 章,就能追完全貌。解答的寫法整理在第 12 章,可用於實務設計與程式碼審查的檢查清單則整理在第 13 章。
圖中實線表示始終成立的關係,虛線表示附帶條件的關係(成立條件寫在詳細頁面中各關係的說明中)。關係的完整清單(共 18 條,附依據與可信度)以及主要概念的定義,彙整在知識地圖詳細頁面(日文)。資料:JSON-LD / Turtle
2. 題目的結構 ── 把五道確認分開來讀
題目的場景,是準備新推出健康照護服務的 G 公司。使用者透過智慧型手機應用程式輸入飲食與體重等資料,取得健康風險判定與飲食菜單建議。系統建置在雲端上,組合了 API 閘道、事件驅動的處理與代管資料庫。
題目原文中的產品名稱與服務名稱都經過抽象化。本文不直接轉載試題冊的圖表,而是改用自己的說法,描述理解設問所需的結構。標示官方解答要點的部分,與實務上的補充會分開呈現。1
flowchart TB
accTitle: 題目全貌
accDescr: 把認證碼、JWT、目標 ID、更新欄位、外部輸入引發的執行,各自當成不同的確認來讀。
A["使用者的請求"] --> B["認證碼的核對"]
B --> C["JWT 的發行與驗證"]
C --> D["使用者 API"]
D --> E["目標 mid 的授權"]
D --> F["更新 status 的授權"]
D -.-> G["把外部輸入寫入日誌"]
G --> H["有漏洞的函式庫"]
H --> I["透過 JNDI 執行外部程式碼"]
圖1:從認證、API 操作到日誌處理,需要判斷是否信任值的地方不只一處。
2.1. 區分認證、權杖驗證與兩種授權
用認證碼確認本人身分、發行並驗證 JWT 之後,對資料與欄位的授權仍然沒有解決。把外部輸入寫入日誌的路徑上,也需要一道不讓輸入被當成命令執行的邊界。
[使用者 ID・密碼]
|
v
[核對 4 位數認證碼] ---- 沒有嘗試次數限制 ----> 暴力破解
|
v
[發行 JWT]
|
v
[JWT 函式庫] ------- 允許 alg=none ------> 竄改使用者 ID
|
v
[使用者 API]
| |
| +-- 把 status 整包傳下去 ----> 屬性層級授權缺失
|
+-- 信任 mid ----------------------------> 物件層級授權缺失
[把外部輸入寫入日誌]
|
v
[有漏洞的函式庫] ---- JNDI/LDAP/HTTP ------> 執行外部程式碼
這裡最重要的,是下面這幾項區分。
| 確認 | 要問的事 | 本題中被突破的例子 |
|---|---|---|
| 認證 | 你是誰 | 4 位數認證碼的暴力破解 |
| 權杖驗證 | 那份身分資訊有沒有被竄改 | alg=none |
| 物件層級授權 | 可不可以存取該使用者的資料 | 抽換 mid |
| 屬性層級授權 | 可不可以變更該欄位 | status=paid |
| 從輸入到執行的邊界 | 外部輸入會不會被解讀成命令 | JNDI Lookup |
上一道確認通過,不構成省略下一道確認的理由。 就算使用者持有正確的 JWT,也不代表可以讀取他人的資料。就算使用者能更新自己的資料,也不代表連付費狀態都可以變更。
只要能把這些階段分開,各設問的答案就不再需要死背。
flowchart TB
accTitle: 認證與授權的差別
accDescr: 就算確認了主體是誰,仍需另外確認是否允許該主體操作目標資料或欄位。
A["認證與權杖驗證"] --> B["確定這是誰的請求"]
B --> C["可不可以存取那份資料"]
C --> D["可不可以變更那個欄位"]
圖2:認證與授權的差別。認證在先,授權是另一道確認。
2.2. 設問2 要靠攻擊者變更的值來區分
設問2 中容易混淆的論點,可以用攻擊者控制的值來整理。
| 攻擊 | 攻擊者變更的值 | 不該信任的地方 | 根本對策 |
|---|---|---|---|
| 竄改 JWT | JWT 標頭的 alg、酬載中的使用者 ID |
權杖自行申報的驗證演算法 | 在伺服器端固定允許的演算法 |
| 取得他人的資訊 | 請求中的 mid |
用戶端指定的目標 ID | 與 JWT 的主體核對,或由 JWT 決定目標 ID |
| 變更為付費使用者 | 規格外的 status |
自動綁定的全部屬性 | 把可更新的屬性做成許可清單 |
| 突破 4 位數認證碼 | otp 的候選 |
不受限制的認證嘗試 | 加入失敗次數限制、延遲與風險判定 |
重要的是不要把這些全部歸納成「驗證輸入值」一句話。
alg是密碼處理的原則mid是物件層級的授權status是屬性層級的授權otp是對線上猜測的耐受度
就算出現在同一個 HTTP 請求裡,要守的理由也各不相同。
3. 設問1 ── 無狀態並不表示連業務資料與失敗次數都不保存
設問1 問的是 RESTful API 設計原則之一,也就是不做工作階段管理的性質。
解答:空格 a 是「無狀態(stateless)」。2
3.1. 依據 ── 光靠每個請求本身就能湊齊處理所需的資訊
所謂無狀態,是指伺服器不必記住前一個請求的對話狀態,光靠每個請求本身就能湊齊處理所需的資訊。在本題中,智慧型手機應用程式會在每個請求的 Authorization 標頭附上 JWT。伺服器驗證這個 JWT,藉此識別該請求的使用者。
3.2. 容易誤解之處 ── 並不是連資料與失敗次數都不保存
容易誤解的地方,是把無狀態讀成「伺服器不保存任何狀態」。實際上,下列狀態照樣會保存。
- 儲存使用者資訊與健康資料的資料庫
- 付費狀態
- 認證碼的值、有效期限與失敗次數
- JWT 的簽章金鑰
- 若採用失效清單的設計,還有那份失效資訊
- 日誌與稽核紀錄
不保存的是:不把僅僅為了延續對話而存在的伺服器端工作階段狀態,當成每次 API 呼叫的前提。
此外,無狀態並不會自動提高安全性。每次都送出 JWT 確實比較容易做水平擴充,但只要 JWT 的驗證寫錯,這個錯誤也會均勻地散布到所有節點。架構上的性質與安全上的正確性是兩回事。
flowchart TB
accTitle: 無狀態與要保存的狀態
accDescr: 就算處理每個請求時不依賴對話歷程,業務資料與失敗次數等狀態仍然要保存。
A["附帶 JWT 的請求"] --> B["僅憑該請求識別使用者"]
B --> C["執行處理並回應"]
D["使用者資訊・付費・失敗次數"] -.-> C
C -.-> E["不需要依賴先前的對話"]
圖3:不依賴對話,與保存業務及安全相關的狀態,兩者可以並存。
4. 設問2(1) ── 計算 4 位數認證碼的平均突破時間
解答:空格 b 是「500」,單位是秒。2
4.1. 依據 ── 把候選數、嘗試速度與有效期間放在一起看
認證 API 在使用者 ID 與密碼正確時,會把 4 位數字寄到電子郵件。之後只要使用者 ID 與 4 位數認證碼一致,就發行 JWT。認證碼從產生起算有效 10 分鐘。
在診斷中,每秒可以嘗試 10 次。要求的是平均幾秒能夠突破。
計算時取「候選數的一半」
4 位數字包含開頭的 0 在內,共有下列 1 萬種。
0000, 0001, 0002, ... , 9999
若正解是均勻選出的,而且依序嘗試不重複的候選,考試中會把平均嘗試次數算成候選數的一半。
平均嘗試次數 = 10,000 ÷ 2 = 5,000 次
平均時間 = 5,000 ÷ 10 次/秒 = 500 秒
用這個近似算出來,就是官方解答的 500 秒。若嚴格地把第 1 次到第 10,000 次平均,會是 5,000.5 次,不過這裡配合考試的解答。
最壞情況要花 1,000 秒,但題目問的是平均。而認證碼的有效期間是 600 秒,比平均突破時間 500 秒更長。這就是被判斷為「很可能遭到突破」的理由。
flowchart TB
accTitle: 4 位數認證碼的時間感
accDescr: 考試的近似算法是平均 500 秒,若嘗試不重複的候選,有效期間 600 秒可查驗六成候選。
A["候選共 10000 種"] --> B["平均約 5000 次"]
B --> C["每秒 10 次約需 500 秒"]
C --> D["短於有效期間 600 秒"]
D -.-> E["600 秒可查驗 6000 個候選"]
圖4:4 位數認證碼的時間感。平均試到候選數的一半,就會在有效期間內命中。
4.2. 實務補充 ── 不能只看失效時間,還要看能嘗試的次數
認證碼的強度,既不是只靠位數決定,也不是只靠有效期間決定。
有效期間內可嘗試的次數
= 每秒的嘗試次數 × 有效期間
= 10 × 600
= 6,000 次
若依序嘗試不重複的值,就能在有效期間內查驗 1 萬種中的 60%。就算設了失效時間,只要沒有限制嘗試次數,還是不夠。
現行的 NIST SP 800-63B 對頻外認證所用的短期祕密要求至少 6 位數,未滿 64 bit 時則必須限制嘗試次數。此外還要求不要把電子郵件用於頻外認證4。考試時要在題目給定的 4 位數、寄送電子郵件這個規格內作答,但實務上做新設計時,連這個前提本身都應該重新檢討。
5. 設問2(2) ── 不讓攻擊者挑選 JWT 的驗證方式
解答要點
設問問的是,修正後的函式庫 Q「對哪一份資料、做什麼樣的驗證」,兩者各限 20 字以內。
解答範例如下。
| 項目 | 解答要點 |
|---|---|
| 驗證對象的資料 | JWT 標頭中 alg 所指定的值 |
| 驗證的內容 | 驗證其值不是 NONE |
這是針對題目原文所述漏洞的直接修正。2 如果只寫「驗證簽章」,答案就變成重複既有已實作的處理。評分講評中也指出了這一類誤答。3
5.1. 依據 ── 既有簽章驗證的「選擇方式」錯了
題目中的 JWT 由標頭、酬載、簽章三個部分組成。
base64url(header).base64url(payload).base64url(signature)
標頭中記錄的簽章演算法是 RS256。酬載中則放了使用者 ID、發行時刻與有效期限。
診斷人員變更了下列兩處。
- 把標頭的
alg從RS256改成NONE - 把酬載中的使用者 ID 改成別的使用者
送出這個 JWT 之後,驗證通過,於是成功冒充了他人。
flowchart TB
accTitle: JWT alg=none 攻擊的流程
accDescr: 攻擊者改掉驗證方式與使用者 ID 後,允許 none 的函式庫就接受了遭竄改的 JWT。
A["取得正確的 JWT"] --> B["把 alg 改成 none"]
B --> C["把 user 改成別的使用者"]
C --> D["函式庫略過簽章驗證"]
D --> E["當成另一位使用者接受"]
圖5:JWT alg=none 攻擊的流程。挑選驗證演算法的人是攻擊者。
none 並不是規格之外的值
RFC 7519 把 alg 為 none、既無簽章也無加密的 JWT 定義為「Unsecured JWT」5。因此,none 這個值並不是規格上完全不存在的東西。
問題在於,只應接受帶簽章 JWT 的 API,卻接受了攻擊者指定的 none。
把有漏洞的處理用概念的方式寫出來,會像下面這樣。
1. 讀取 JWT 標頭
2. 看標頭中寫的 alg,選擇驗證方式
3. 若 alg 是 none,就不進行簽章驗證
4. 信任酬載中的使用者 ID
等於是讓攻擊者控制的輸入,來挑選安全強度本身。
5.2. 實務補充 ── 在伺服器端固定允許的演算法
這裡必須把考試的解答與實務上的建議分開。
RFC 8725 要求 JWT 函式庫讓呼叫端指定一組允許的演算法,並且不得使用該集合以外的演算法6。也就是下面這種想法。
不好的想法:
token.header.alg != "none" 就接受
好的想法:
只在包含於 serverConfig.allowedAlgorithms 時才接受
例:allowedAlgorithms = ["RS256"]
就算只拒絕 none,仍可能留下其他弱演算法,或是把公開金鑰方式與共用金鑰方式搞混的演算法混淆問題。原則是不要用否定形去疊加接受條件,而要把允許的條件收窄並固定下來。
驗證 JWT 時,除了演算法以外,還要依用途至少確認下列項目。
| 項目 | 要確認的事 |
|---|---|
| 簽章 | 能否用預期的金鑰與演算法驗證 |
iss |
是否為信任的發行者 |
aud |
是否是發給這個 API 的權杖 |
exp |
是否仍在有效期限內 |
nbf |
是否早於可開始使用的時刻 |
sub 或使用者 ID |
是否為應用程式上有效的主體 |
| 權杖種類 | 是否把 ID 權杖與存取權杖之類搞混 |
本題中酬載的鍵名是 user,但實務上要使用標準的 sub,或是明確定義自訂宣告的意義。
flowchart TB
accTitle: 安全的 JWT 驗證與危險的 JWT 驗證
accDescr: 不讓權杖的申報決定驗證方式,而是在滿足伺服器端的允許條件後才驗證簽章與各項宣告。
A["接收 JWT"] --> B{"是否為伺服器允許的 alg"}
B -->|"否"| X["拒絕"]
B -->|"是"| C["驗證簽章與各項宣告"]
C --> D["視為已驗證的主體"]
Y["危險的處理"] -.-> Z["依申報的 none 略過驗證"]
圖6:安全的驗證與危險的驗證。實務上要把允許的演算法收窄並固定下來。
5.3. 區分簽章與保密 ── base64url 不是加密
JWT 還有另一個常見的誤解。標頭與酬載是用 base64url 表示的,但那不是加密。任何人都能解碼閱讀。
簽章所保證的,只有在能正確驗證的前提下,內容自發行後未遭竄改這一點。這並不表示可以把想保密的個人資料放進帶簽章 JWT 的酬載。
flowchart TB
accTitle: JWT 的簽章與保密是兩回事
accDescr: base64url 是第三方也讀得到的表示法,簽章的竄改偵測與機密性要分開思考。
A["帶簽章的 JWT"] --> B["讀取標頭與酬載"]
B --> C["解碼 base64url"]
C --> D["內容並不保密"]
A --> E["正確驗證簽章"]
E --> F["確認是否遭到竄改"]
圖7:簽章是用來偵測竄改的,不是把內容藏成讀不到的形式。
6. 設問2(3) ── 正確的 JWT 與正確的操作對象是兩回事
解答要點
表5 的底線②問的是,要在共用模組 P 的呼叫處理中追加什麼處理,限 40 字以內。
解答範例如下。2
驗證 JWT 所含的使用者 ID 是否與 mid 的值一致的處理
設問指定的追加位置不是共用模組 P 本身,而是呼叫 P 的「P 呼叫處理」。要在這裡追加 JWT 與 mid 的一致性確認。1
實務上的目標,是連同 P 這一側,在抵達資料的共用層一致地執行授權。這樣不論 GET 或 PUT,或是今後使用 P 的其他 API,都比較容易套用同一套授權。若只是把比對處理複製到各個畫面或端點,就會出現漏寫。
6.1. 依據 ── JWT 的主體與操作對象的 mid 是分別傳入的
接下來是不竄改 JWT 本身的攻擊。
使用者 API 會在 GET 或 PUT 時接收名為 mid 的使用者 ID。共用模組 P 則依這個 mid 從資料庫取得或更新對應的使用者資訊。
攻擊的結構很單純。
JWT 的使用者 ID: user01 ← 正確簽章的 JWT
請求中的 mid: user02 ← 攻擊者變更
JWT 的簽章是正確的,所以認證會通過。但 API 直接信任 mid=user02,於是回傳 user02 的資訊。
這正是 OWASP API Security Top 10 2023 所說的 Broken Object Level Authorization(BOLA) 的典型例子。當程式用使用者指定的物件 ID 去存取資料時,每一次都必須確認對該物件的授權7。
flowchart TB
accTitle: BOLA 攻擊
accDescr: 正確 JWT 的主體與請求的 mid 不同,卻只憑 mid 就回傳了他人的資訊。
A["簽章正確的 JWT(user01)"] --> C["使用者 API"]
B["變更後的 mid(user02)"] --> C
C --> D["未與主體核對"]
D --> E["取得或更新 user02 的資料"]
圖8:BOLA 攻擊。認證通過了,卻沒有確認授權。
6.2. 實務補充 ── 本人專用 API 不接收 mid
如果 API 只取得或更新使用者本人的資訊,就沒有必要從用戶端接收使用者 ID。
GET /users/me
Authorization: Bearer <JWT>
伺服器端則從已驗證的 JWT 取出主體。
principal = validateJwt(request.authorization)
userId = principal.subject
return repository.getUser(userId)
更新也一樣。
principal = validateJwt(request.authorization)
input = validateProfileUpdate(request.body)
repository.updateProfile(
userId = principal.subject,
name = input.name,
age = input.age
)
比對處理只要寫了就能防住。但如果設計成不從外部接收目標 ID,就能從根本減少「忘了寫比對處理」這一類的缺陷。
如果管理者需要操作他人的使用者資訊,就像下面這樣拆開。
PUT /users/me 一般使用者用
PUT /admin/users/{userId} 管理者用
管理者用的路徑要求另一套權限、稽核日誌,必要時還要重新認證。比起「在一般使用者 API 上只為管理者加上例外」,這種做法更容易看清授權原則的邊界。
flowchart TB
accTitle: BOLA 的防範方式
accDescr: 確認已驗證主體與 mid 是否一致,或在本人專用 API 由主體推導目標,減少漏寫比對。
A["已驗證 JWT 的主體"] --> B{"目標 ID 的決定方式"}
B -->|"接收 mid"| C{"是否與主體一致"}
C -->|"是"| D["操作自己的資料"]
C -->|"否"| E["拒絕"]
B -->|"本人專用 API"| F["由主體推導目標 ID"]
F --> D
圖9:BOLA 的防範方式。不接收 mid,或是與 JWT 的主體核對。
6.3. 確認 ──「是誰」與「可以做什麼」是兩回事
不論考試或實務,下面的說法都很好用。
- 認證:是誰
- 授權:這個人可以做什麼
JWT 簽章驗證通過,只到「可以信任這個權杖所代表的主體」為止。「那個主體可以讀取 user02」必須另外確認。
7. 設問2(4) ── 只接收允許更新的欄位
解答:空格 c 是「共用模組 P」。2
7.1. 依據 ── 規格外的 status 被傳到了哪裡
使用者 API 的規格中,定義了下列更新用參數。
mid 使用者 ID
name 姓名
age 年齡
然而診斷人員追加了規格中沒有的下列值。
status=paid
結果免費使用者的狀態就變成了付費使用者。
根據題目原文,服務 L 沒有驗證收到的參數,而是全部傳給共用模組 P。P 的做法則是直接就能更新資料庫。
由此可知,接收規格外的值、連使用者狀態都能更新的傳送對象,就是共用模組 P。
flowchart TB
accTitle: Mass Assignment
accDescr: 連規格外的 status 都傳給共用模組去更新,使用者就能自行變更付費狀態。
A["規格是 mid・name・age"] --> B["追加 status=paid"]
B --> C["把全部參數傳給 P"]
C --> D["整包套用到內部資料"]
D --> E["付費狀態變成 paid"]
圖10:Mass Assignment。規格外的屬性整包反映到內部物件上。
7.2. 與 BOLA 的差別 ── 守的不是對象,而是對象裡的欄位
前一章抽換 mid 與這次追加 status 看起來相似,但要守的粒度不同。
| 漏洞 | 攻擊者變更的東西 | 原本應該確認的事 |
|---|---|---|
抽換 mid |
目標物件 | 這位使用者是否可以存取這筆使用者記錄 |
追加 status |
物件內的屬性 | 這位使用者是否可以變更這個欄位 |
OWASP API Security Top 10 2023 把後者歸為 Broken Object Property Level Authorization,並把過去的 Mass Assignment 納入這個分類8。
7.3. 實務補充 ── 把更新用的輸入型別做成許可清單
有漏洞的實作,概念上像下面這樣。
entity = repository.find(body.mid)
bindAllProperties(entity, body)
repository.save(entity)
就算畫面上只有 name 與 age 的輸入欄位,攻擊者也能直接組出 HTTP 請求。UI 上沒有的欄位並不構成安全邊界。
安全的實作會明確列出可更新的欄位。
input = parseExactSchema(body, fields = ["name", "age"])
entity = repository.find(authenticatedUserId)
entity.name = input.name
entity.age = input.age
repository.save(entity)
這裡重要的有兩點。
- 更新用的輸入型別中,只放使用者可以變更的欄位
- 對規格外的未知欄位,不要忽略,盡可能當成錯誤拒絕
默默忽略未知欄位雖然能藏住攻擊失敗這件事,卻也會漏看用戶端的實作錯誤與攻擊徵兆。只要沒有相容性上的理由,用嚴格的結構描述拒絕會更容易調查。
flowchart TB
accTitle: 欄位層級的授權
accDescr: 把更新用輸入型別限定為 name 與 age,付費狀態則由已驗證的支付通知走專用路徑更新。
A["個人資料更新請求"] --> B{"是否只有 name 與 age"}
B -->|"是"| C["只更新允許的欄位"]
B -->|"否"| D["拒絕未知的欄位"]
E["已驗證的支付通知"] --> F["核對 paymentId 並防止重複"]
F --> G["以專用路徑更新 status"]
圖11:欄位層級的授權。用許可清單限制可更新的欄位,付費狀態則走另一條路徑變更。
7.4. 付費狀態只依已驗證的支付結果來變更
status=paid 並不是使用者個人資料的一部分,而是由「支付成功」這個伺服器端事實推導出來的狀態。
使用者的個人資料更新
-> 只能變更 name / age
來自支付服務的已驗證通知
-> 核對 paymentId
-> 防止重複處理
-> 把 status 變更為 paid
就算存放在同一個資料庫欄位,變更的權限與路徑仍然不同。把內部實體直接拿來當成外部 API 的輸入型別,這道邊界就會消失。
7.5. 共用元件要連授權與輸入限制一起包進去
本題中出現了兩個共用元件:JWT 管理函式庫 Q 與共用模組 P。
共用元件有很大的好處。
- 只要改一處,就能把修正反映到所有使用它的 API
- 不必把授權與驗證的實作重複寫進每個功能
- 可以把測試對象集中起來
- 可以統一日誌與稽核的格式
另一方面,錯誤也會擴散到全體。
- 函式庫 Q 一旦接受
alg=none,所有使用 JWT 的 API 都會陷入危險 - 共用模組 P 一旦接受任意的
mid或status,GET 與 PUT 兩邊都會陷入危險 - 有漏洞的函式庫 H 一旦用在基礎層,所有把 HTTP 標頭寫入日誌的路徑都會變成攻擊面
因此,應該共用化的並不只是單純的資料存取。必須把安全不變條件共用化,並針對該共用元件本身做嚴格的驗證。
例如,把 P 的契約訂成下面這樣。
P.getOwnUser(authenticatedSubject)
P.updateOwnProfile(authenticatedSubject, ProfileUpdate{name, age})
像下面這種低階 API,不要直接對一般呼叫端公開會比較安全。
P.getUser(arbitraryMid)
P.updateUser(arbitraryMap)
需要用到後者的,只有管理處理等有限的路徑。把低階的自由度發放給所有 API,設計上就變成期待每個呼叫端每次都正確使用。
flowchart TB
accTitle: 把共用元件的契約收窄
accDescr: 把授權與輸入型別納入共用元件的契約,就能減少對呼叫端每次都用對的依賴。
A["一般使用者用 API"] --> B["已驗證主體與更新 DTO"]
B --> C["在共用元件統一授權"]
C --> D["操作獲准的資料"]
E["任意 ID・任意 Map 的操作"] -.-> F["分離到管理用等限定路徑"]
C -.-> G["嚴格測試共用元件"]
圖12:要共用化的不只是資料存取,還包括應該守住的授權與輸入條件。
8. 設問2(5) ── 計數失敗次數,擋下暴力破解
針對 4 位數認證碼的暴力破解,要在 30 字以內回答表5 空格 d 該填入的處理。門檻值是 10。
解答範例如下。2
連續失敗次數超過門檻就鎖定帳戶的處理
8.1. 依據 ── 失敗次數是安全判斷所需的狀態
這一點與設問1 的無狀態並不矛盾。不把 API 呼叫的對話狀態當成伺服器工作階段保存,與把安全判斷所需的失敗次數持久化,是兩件不同的事。
flowchart TB
accTitle: 有無嘗試次數限制
accDescr: 保存失敗次數並在超過門檻時鎖定,就能擋下不受限制的線上猜測。
A["認證碼核對失敗"] --> B["更新帳戶的失敗次數"]
B --> C{"是否超過門檻"}
C -->|"是"| D["鎖定帳戶"]
C -->|"否"| E["允許剩下的嘗試"]
F["沒有限制就能一直試下去"] -.-> A
圖13:有無次數限制。靠失敗次數限制,才能在實務上擋下暴力破解。
8.2. 實務補充 ── 也要防範鎖定造成的拒之門外
以帳戶為單位的次數限制確實必要,但如果攻擊者知道他人的使用者 ID,就能故意失敗到超過門檻,把正規使用者擋在門外。因此實務上要組合下列做法。
| 控制 | 作用 |
|---|---|
| 帳戶單位的失敗次數 | 擋下針對單一帳戶的暴力破解 |
| 階段性的等待時間 | 容許正規使用者輸入錯誤,同時拖慢攻擊速度 |
| 來源 IP・裝置・ASN 等的控制 | 抑制對大量帳戶各試少數次的攻擊 |
| 風險導向的判定 | 對不同於平常的地區、裝置、速度施加更強的限制 |
| 對使用者的通知 | 讓使用者能察覺攻擊或誤操作 |
| 安全的復原程序 | 別讓解除鎖定的窗口變成攻擊路徑 |
此外,重新寄送認證碼時不可以把失敗次數歸零。否則攻擊者每呼叫一次重寄 API,就能讓嘗試額度復活。現行的 NIST SP 800-63B 也要求即使產生了新的認證祕密,也不要重設失敗次數4。
8.3. 重新發行、重複使用與寄送 API 要成組防護
題目原文的重點在有效期限,但實務上還需要下列做法。
- 成功使用過的認證碼要立即失效
- 拒絕同一組認證碼的重複使用
- 不要把認證碼本身寫進日誌
- 讓回應無法從核對成敗推測出使用者是否存在
- 對寄送認證碼的 API 也設次數限制
既然用的是短祕密,就不能把安全性全交給隨機產生。
flowchart TB
accTitle: 認證碼的對策
accDescr: 除了候選數與有效期限,還要組合失敗次數的保存、成功後失效、通知與復原程序。
A["發行・重新發行認證碼"] --> B["不重設失敗次數"]
B --> C["限制次數與速度後核對"]
C --> D{"核對是否成功"}
D -->|"是"| E["立即讓認證碼失效"]
D -->|"否"| F["記錄失敗・延遲・鎖定"]
F --> G["通知與安全的復原程序"]
A -.-> H["寄送 API 也限制、不記錄認證碼"]
圖14:認證碼的對策。除了位數與失效時間,還要組合嘗試控制與維運。
9. 設問3(1) ── 用存取日誌確認驗證命令確實執行了
解答要點
設問3(1) 問的是,在測試伺服器上實作什麼,才能確認命令確實執行了。
解答範例如下。
記錄並確認對測試伺服器 index.html 之存取的機制2
9.1. 依據 ── 觀測到整條鏈一路抵達最後的驗證命令
服務上線後,廣泛使用的開放原始碼函式庫 H 被公布了重大漏洞 V。題目原文的流程如下。
- 攻擊者把含有 JNDI Lookup 的字串放進 HTTP 標頭送出
- 受攻擊的伺服器把那個值輸出到日誌
- 有漏洞的函式庫評估 JNDI Lookup,向攻擊用的 LDAP 伺服器查詢
- LDAP 回應傳回攻擊用 HTTP 伺服器的 URL
- 受攻擊的伺服器取得類別檔案並執行命令
這可以讀成隱去專有名稱的 Log4Shell(CVE-2021-44228)型攻擊。Apache 的說明中也把它描述為:只要攻擊者能控制日誌訊息或參數,就能執行從 LDAP 伺服器載入之任意程式碼的漏洞9。
flowchart TB
accTitle: Log4Shell 型漏洞的確認流程
accDescr: 不只看 JNDI 參照或類別取得,還要用日誌確認驗證命令取得了 index.html。
A["HTTP 標頭中的外部輸入"] --> B["目標伺服器的日誌處理"]
B --> C["透過 JNDI 向 LDAP 查詢"]
C --> D["接收類別取得來源的 URL"]
D --> E["目標伺服器取得類別"]
E --> F["在目標伺服器執行驗證命令"]
F --> G["取得測試伺服器的 index.html"]
G --> H["記錄並確認該次存取"]
圖15:Log4Shell 型漏洞的確認流程。不用破壞性命令,而是用 HTTP 存取紀錄確認抵達。
驗證命令引發的只是無害的 HTTP 存取
G 公司會執行不影響系統的驗證程式碼,確認能否從外部利用漏洞 V。驗證程式碼執行的命令,就只是取得測試伺服器的 index.html。
只要 Web 伺服器的存取日誌留下來自受攻擊伺服器的 GET,就至少能確認下面這條鏈確實走通了。
外部 HTTP 請求
-> 日誌處理
-> JNDI Lookup
-> LDAP 回應
-> 取得類別
-> 執行驗證命令
-> 對測試伺服器的 HTTP 存取
9.2. 為什麼看的是測試伺服器的日誌,而不是畫面顯示
攻擊目標是伺服器,使用者的瀏覽器畫面不一定會有變化。而且就算漏洞存在,中途的對外通訊也可能被防火牆擋下。
只要在測試伺服器這一側記錄存取,就能成為「從受攻擊伺服器抵達外部」這件事的可觀測證據。
9.3. 實務補充 ── 取得許可,以最小副作用來確認
在實務上做同類驗證時,務必遵守下列事項。
- 取得目標系統擁有者的明確許可
- 採用不影響正式環境,或影響在可接受範圍內的驗證方式
- 不使用寫入、刪除、變更設定之類的破壞性命令
- 驗證用的網域與伺服器由自家公司管理
- 記錄驗證時刻、來源、目標與預期的回呼
- 驗證後撤除臨時的 LDAP・HTTP 伺服器與憑證
「確認可以執行任意程式碼」與「執行任意危險的程式碼」不是同一件事。要把副作用壓到足以達成目的的最小程度。
flowchart TB
accTitle: 把驗證限制在最小副作用
accDescr: 取得許可後決定無害的確認方式與觀測條件,執行後撤除驗證環境與憑證。
A["擁有者的明確許可"] --> B["決定影響範圍與觀測條件"]
B --> C["在自己管理的伺服器記錄存取"]
C --> D["與預期的時刻・來源核對"]
D --> E["撤除驗證環境・憑證"]
B -.-> F["不做寫入・刪除・變更設定"]
圖16:先決定想觀測的事實,再把無害的確認與善後一併納入驗證程序。
10. 設問3(2)~(4) ── 決定 WAF 的檢查位置、模式與動作
10.1. 設問3(2) ── 兩個檢查對象都是 Header
服務 N 的 WAF 可以把 GET、POST、PUT、ANY、Header、COOKIE、Multipart 選為檢查對象。
攻擊程式碼放在名為 x-api-version 的 HTTP 標頭的值裡。因此,表6 的空格 e 與 f 都是 Header。2
依據是攻擊字串所在的位置
這裡考的與其說是一般知識,不如說是讀懂題目原文的資料流。
攻擊字串的存放位置:
x-api-version 標頭
|
v
WAF 的檢查對象:
Header
本題中的 ANY 是指檢查所有方法的參數值,與檢查標頭的 Header 不同。1
既不是 GET 參數,也不是 POST 本文。不要看著 WAF 的功能一覽,用「看起來像攻擊所以選 ANY」來作答,而要回答題目原文中攻擊者放入值的位置。
flowchart TB
accTitle: 選擇 WAF 的檢查位置
accDescr: 不看攻擊名稱,而是依題目原文中攻擊字串的存放位置決定 WAF 的檢查對象。
A["讀出攻擊字串的存放位置"] --> B["x-api-version 標頭"]
B --> C["檢查對象是 Header"]
C --> D["改成含大小寫的模式"]
E["ANY 是所有方法的參數"] -.-> C
圖17:不要從攻擊名稱推測,而要把標頭這個存放位置對應到檢查對象。
10.2. 設問3(3) ── 每個字母都同時容許大寫與小寫
最初的方案,概念上是下面這組規則。
Header \Wjndi\W 阻斷
Header \Wldap\W 阻斷
但只要像 jNdI 那樣替換大小寫,就能繞過只有小寫的單純模式。
設問3(3) 的解答範例是下列任一種。2
\W[jJ][nN][dD][iI]\W
\W(j|J)(n|N)(d|D)(i|I)\W
試題冊上的反斜線在日文環境的字形下可能看起來像日圓符號,但作為正規表示式就是 \W。\W 會比對英數字與底線以外的字元。在 JNDI Lookup 的語法中,jndi 前後會出現 ${ 或 : 之類的非單字字元,所以連同這些一起比對。
用同樣的想法,ldap 這一側也可以改成忽略大小寫的形式。
\W[lL][dD][aA][pP]\W
10.3. 設問3(4) ── 偵測是為了避免誤阻斷
關於修改後的 WAF 規則,登錄資訊處理安全確保支援士 Z 先生建議,在正式上線後的一段期間內,把動作設為「偵測」而不是「阻斷」。
設問問的是設為偵測的好處,以及為了把損害降到最低應執行的內容,兩者各限 25 字以內。
解答範例如下。2
| 項目 | 解答要點 |
|---|---|
| 好處 | 可以避免因誤判而遭到阻斷 |
| 應執行的內容 | 收到警示後詳查是否為攻擊 |
依據 ── 偵測必須與詳查警示的維運成組使用
在偵測模式下,符合規則的通訊照樣放行,同時寫入日誌並發出警示。就算正常的 API 呼叫中碰巧含有 jndi 或 ldap 這類字串,也不會突然讓業務中斷。
相對地,維運這一側需要下列流程。
收到警示
|
v
確認對應的請求
|
+-- 正常通訊 -> 收窄規則、研議例外條件
|
+-- 攻擊 -> 隔離對象、保全日誌、調查影響、轉為阻斷
如果沒有人看警示,偵測模式就沒有防禦效果。偵測必須與觀測並判斷的維運成組使用。
10.4. 實務補充 ── 先觀測、調整,再轉為阻斷
一般的導入程序如下。
- 以偵測模式套用在實際流量上
- 把誤判與正確偵測分類
- 調整檢查的標頭、路徑、API 與字元邊界等
- 確認對正常通訊的影響在可接受範圍內
- 轉為阻斷模式
- 監控阻斷件數與業務影響
不過,這是平時的原則。若漏洞重大、正遭到實際利用,而且沒有替代手段,也可能判斷入侵造成的損害大於誤判導致的中斷,於是一開始就採取阻斷。在考試的情境中,為了確認服務能否照常使用,才先選擇偵測。
flowchart TB
accTitle: WAF 從偵測轉為阻斷
accDescr: 詳查偵測期間的警示,完成誤判調整與攻擊處置後轉為阻斷,並在之後持續監控。
A["以偵測模式觀測通訊"] --> B{"警示的內容是什麼"}
B -->|"誤判"| C["調整規則或例外條件"]
C --> A
B -->|"攻擊"| D["隔離・保全日誌・調查影響"]
D --> E["轉為阻斷"]
A --> F["確認對正常通訊的影響"]
F --> E
E --> G["監控阻斷件數與業務影響"]
圖18:從偵測轉為阻斷。先觀測、調整,確認影響可接受之後再轉為阻斷。
11. 實務上的漏洞應對 ── 用 WAF 爭取時間,同時推進更新與調查
題目原文的情境是:函式庫 H 的官方網站上還沒有修正版,也還沒有暫定對策,而雲端業者的完整 WAF 規則最多要等 72 小時。所以 G 公司自行確認影響,至少先暫時擋下已經掌握的模式。
考試問的是暫定 WAF 規則與它的維運,但 WAF 並不會修好漏洞本身。 影響確認、暫定緩解與根本修正不必互相等待,能並行的部分就一起推進。包含實務補充在內的整體應對如下。
| 階段 | 目的 | 本題與實務上的做法 |
|---|---|---|
| 影響確認 | 判斷自家公司是否真的有危險 | 用無害的回呼確認能否從外部利用 |
| 暫定緩解 | 為修正爭取時間 | WAF 規則、偵測與阻斷、限制對外通訊 |
| 根本修正 | 移除有漏洞的成因 | 更新到修正版函式庫 |
| 事後確認 | 調查是否已經遭到利用 | 調查 WAF、應用程式、DNS、Proxy 等的日誌 |
| 防止再發 | 讓下次的判斷更快 | 相依關係盤點、SBOM、更新程序、聯絡管道 |
11.1. 不要把暫定規則當成完整的 Log4Shell 對策
考試要答的,是對應題目原文所示規避手法的正規表示式。在實際攻擊中,還可能出現字串拆分、其他 Lookup、編碼、其他通訊協定等,光靠特徵碼難以涵蓋的變形。
因此,它在實務上的定位如下。
- 用 WAF 暫時擋下目前已掌握的攻擊模式
- 調查是否真的含有受影響的函式庫
- 限制對外的 LDAP、RMI 與不必要的 HTTP 通訊
- 更新到修正版
- 更新後仍要確認日誌,調查是否遭到入侵
WAF 是在修正版推出之前爭取時間的一層。
flowchart TB
accTitle: WAF 的定位
accDescr: 偵測放行通訊並加以觀測,阻斷與對外通訊限制提供緩解,根本成因則靠更新移除。
A["重大漏洞公布"] --> B["影響確認與暫定應對"]
B --> C["以偵測取得日誌與警示"]
B --> D["阻斷・限制對外通訊"]
C --> E["依觀測結果調整與處置"]
D --> F["更新到修正版函式庫"]
E --> F
F --> G["事後確認是否遭到入侵"]
圖19:WAF 的定位。WAF 是等修正版推出前的緩衝,根本對策是更新。
11.2. 平時的準備 ── 掌握所用的函式庫與部署位置
題目原文中提到,G 公司就算向 F 公司詢問是否使用函式庫 H,也因為需要詳細的組態分析而要花時間才能回覆。
實務上,若等到重大漏洞公布後才開始找 JAR 檔案,應對就會延遲。至少下列資料應該平時就備妥。
- 直接相依與遞移相依的清單
- 交付物中實際包含的元件與版本
- 部署到哪些服務、容器與裝置
- 更新相依函式庫並重新建置、重新散發的程序
- 核准緊急變更的聯絡管道
- 對外通訊的允許對象,以及停用時的影響
- 日誌的保存位置與搜尋方式
SBOM 本身不是目的,而是為了在短時間內回答「這個漏洞會影響哪些運轉中的系統」而建立的索引。
flowchart TB
accTitle: 把相依關係連到運轉中的系統
accDescr: 平時就把相依元件與部署位置對應起來,加快漏洞公布後的調查與更新判斷。
A["直接・遞移相依的清單"] --> B["交付物的實際版本"]
B --> C["運轉中的服務與部署位置"]
C --> D["短時間內判斷受影響對象"]
D --> E["核准・重新建置・重新散發"]
C -.-> F["通訊對象與日誌保存位置也要掌握"]
圖20:SBOM 的目的不是蒐集,而是快速判斷影響範圍與更新方式的索引。
11.3. 事後確認 ── 不能更新完就結束入侵調查
漏洞公布前後可能早就遭到攻擊。就算更新到修正版、擋下今後的利用,已經外洩的憑證與被植入的後門也不會因此消失。
如果是 Log4Shell 型,至少要從下列角度調查。
- 含有 JNDI 或 LDAP 相關可疑字串的 HTTP 請求
- 從應用程式伺服器往外部 LDAP、RMI、HTTP 的通訊
- 與平常不同的子處理程序啟動
- 可疑的 JAR、class、指令碼與執行檔的建立
- 對雲端憑證或環境變數的存取
- 更新前後的認證、權限變更與對外傳送
重要的是不要只憑 WAF 日誌就斷定「沒有遭到攻擊」。因為還有不經過 WAF 的內部路徑,也有過去根本沒保存下來的日誌。
flowchart TB
accTitle: 把更新與入侵調查分開
accDescr: 更新到修正版以防止今後的利用,與調查是否已經遭到入侵,兩件事都要分別處理。
A["更新到修正版"] --> B["阻止今後的利用"]
C["調查更新前後的紀錄"] --> D["核對通訊・處理程序・檔案"]
D --> E["確認憑證與對外傳送"]
B -.-> F["過去的入侵不會消失"]
F -.-> C
圖21:修正成因與調查已經發生的入侵,要分開推進。
12. 解答的寫法 ── 用題目原文的用語回答與規格的差異
這一題與其說是知識題,不如說是讀出規格與實作差異的題目。
12.1 把表中的「規格」與「實作」分開
在 status 的問題中,API 規格上沒有的值在實作上卻通過了。
規格:
mid / name / age
實作:
把收到的參數全部送給 P
只要看見這個差異,就知道空格 c 是共用模組 P。
12.2 在攻擊者變更的值上劃線
各項攻擊中被變更的值如下。
- JWT 標頭的
alg - JWT 酬載中的使用者 ID
- API 參數的
mid - 規格外的
status - 認證 API 的
otp - HTTP 標頭的
x-api-version
設問幾乎全都在問「那個值應該在哪裡被驗證」。
12.3 把解答還原成題目原文的用語
在實務上可以叫它們「BOLA」「Mass Assignment」「rate limiting」。但設問要的,是配合題目原文結構的具體處理。
不好的例子:
適當地執行授權。
好的例子:
驗證 JWT 所含的使用者 ID 是否與 mid 的值一致。
不好的例子:
做好暴力破解的對策。
好的例子:
連續失敗次數超過門檻就鎖定帳戶。
光是知道抽象名稱,並不能寫出在字數內可以評分的答案。
12.4 WAF 要追的是「值放進了哪裡」
WAF 的檢查對象不是從攻擊種類推測,而是由攻擊字串的存放位置決定。
放進了 x-api-version 標頭
↓
檢查對象是 Header
評分講評指出整體正答率屬於平均水準,設問2(2) 與設問3(1) 則偏低。3 設問3(1) 正答率偏低,也是因為不少答案對不上圖6 的攻擊流程。光是把攻擊步驟用箭頭重新寫一遍,就能看出應該觀測什麼。
flowchart TB
accTitle: 從題目原文回到解答
accDescr: 追查規格與實作的差異、被變更的值、在哪裡信任了處理,再用題目原文的用語寫出具體處理。
A["把規格與實作並排"] --> B["找出被變更的值"]
B --> C["追查在哪裡信任了它"]
C --> D["決定要追加的處理"]
D --> E["還原成題目原文的用語與字數"]
圖22:不只是背用語,而要追著值的流向,寫成具體的處理。
13. 實務 API 審查中可用的檢查清單
這是把本題帶回實際設計與程式碼審查時可用的檢查清單。
JWT 驗證
- 允許的簽章演算法已在伺服器設定中固定
- 會拒絕
none與非預期的演算法 - 依用途驗證簽章、
iss、aud、exp、nbf - 不會把 ID 權杖、存取權杖、重新整理權杖搞混
- 備有金鑰輪替與失效時的程序
- 沒有把應保密的資訊放進 JWT 酬載
物件層級授權
- 變更請求中的 ID 時,無法抵達他人的資料
- 清單、明細、更新、刪除、下載全部都做了授權
- 授權不是在畫面上做,而是在抵達資料的共用層執行
- 本人專用 API 已評估能否由權杖推導目標 ID
- 管理者用的操作與一般使用者用 API 的原則是分開的
屬性層級授權
- 外部輸入型別與資料庫實體是分開的
- 可更新的欄位以許可清單逐一列出
- 規格外的屬性會被拒絕或留下稽核紀錄
- 權限、付費、核准、擁有者等狀態無法由使用者輸入變更
- 回應中也沒有包含不必要的機密屬性
認證嘗試
- 有以帳戶為單位的失敗次數限制
- 有階段性延遲或以來源為單位的控制
- 重新發行認證碼不會重設失敗次數
- 認證碼只能使用一次
- 不把認證碼與密碼寫進日誌
- 解除鎖定與復原程序沒有變成另一條弱認證路徑
重大的相依函式庫漏洞
- 能把運轉中的服務與相依版本對應起來
- 備有以無害方式驗證影響的程序
- 能套用 WAF 或限制對外通訊等暫定對策
- 有由負責人確認偵測警示的維運機制
- 備有更新到修正版的緊急發布路徑
- 會從日誌調查更新前是否已遭到利用
flowchart TB
accTitle: 審查要試的是正常情境之後的事
accDescr: 不只確認正常請求會成功,還要分別確認變更 ID 與欄位、反覆嘗試,以驗證共用層的對策。
A["確認正常請求會成功"] --> B["只變更目標 ID 再確認"]
B --> C["追加未知的更新欄位再確認"]
C --> D["反覆進行認證失敗與重新發行"]
D --> E["確認拒絕・記錄・復原"]
E -.-> F["把它們當成各自獨立的確認"]
圖23:以正常情境的成功為起點,確認各道邊界是否真的擋得下來。
14. 總結 ── 不要省略下一道信任邊界
令和6年度春季下午問1,是一道要把 API 安全的各項論點逐一分離來讀的題目。
使用 JWT 並不代表認證就是安全的。一旦讓攻擊者挑選簽章演算法,使用者 ID 就會被改寫。
JWT 的簽章正確並不代表授權正確。只要信任請求中的 mid,正規使用者就能存取他人的資訊。
能更新自己的物件並不代表可以變更所有屬性。一旦自動綁定 status 這類內部狀態,權限與付費狀態就會被改寫。
認證碼有有效期限並不代表它耐得住暴力破解。必須計算候選數與嘗試速度,並限制失敗次數。
在 WAF 加入規則並不代表漏洞已經修好。要用偵測與阻斷爭取時間、確認影響,最終還是要更新函式庫。
flowchart TB
accTitle: 漏洞與對策的對應表
accDescr: 依權杖與認證嘗試、對象與更新欄位、外部輸入引發的執行,各道邊界分別備妥對策。
A["區分要信任的邊界"] --> B["權杖與認證嘗試"]
A --> C["目標資料與更新欄位"]
A --> D["外部輸入引發的執行"]
B --> E["固定允許的 alg・限制嘗試"]
C --> F["核對主體・限定更新 DTO"]
D --> G["暫定緩解與函式庫更新"]
圖24:漏洞與對策對應表。依被突破的邊界分別準備對策。
貫穿這道題目的原則只有一個。
不要把上一道驗證通過,當成省略下一道信任邊界的理由。
本系列先前的文章解說了令和5年秋季下午問1 的儲存型 XSS,以及令和5年秋季下午問2 從訪客用 Wi-Fi 帶出資訊。關於整個網站的確認觀點,也請參考把 IPA《安全的網站製作方式》當成檢查清單來用。
flowchart TB
accTitle: 最終總結
accDescr: 不可以因為前一道確認通過,就省略下一道確認或維運上的對策。
A["上一道驗證通過"] --> B{"下一道確認也滿足嗎"}
B -->|"另外確認"| C["逐道邊界累積判斷"]
B -->|"因為通過所以省略"| D["留下未確認的邊界"]
C --> E["反映到日常的設計與驗證"]
圖25:最終總結。信任邊界要逐道確認,不能省略其中任何一道。
參考連結
-
IPA,令和6年度 春季 資訊處理安全確保支援士考試 下午 試題冊。本文所討論的題目原文。 ↩ ↩2 ↩3 ↩4
-
IPA,令和6年度 春季 資訊處理安全確保支援士考試 下午 解答範例。各設問的官方解答範例。 ↩ ↩2 ↩3 ↩4 ↩5 ↩6 ↩7 ↩8 ↩9 ↩10 ↩11 ↩12
-
IPA,令和6年度 春季 資訊處理安全確保支援士考試 下午 評分講評。對正答率與誤答傾向的說明。 ↩ ↩2 ↩3
-
NIST,SP 800-63B: Authentication and Authenticator Management。列出短期祕密的位數、嘗試次數限制、重新發行時的失敗次數,以及不把電子郵件用於頻外認證等要求。 ↩ ↩2
-
RFC Editor,RFC 7519: JSON Web Token (JWT)。包含 Unsecured JWT 與
alg=none的 JWT 規格。 ↩ -
RFC Editor,RFC 8725: JSON Web Token Best Current Practices。規定固定允許演算法,以及驗證發行者、主體與 audience 等內容的 BCP。 ↩
-
OWASP,API1:2023 Broken Object Level Authorization。說明必須針對使用者指定的每個物件 ID 確認授權。 ↩
-
OWASP,API3:2023 Broken Object Property Level Authorization。說明包含 Mass Assignment 在內的屬性層級授權缺失及其對策。 ↩
-
Apache Logging Services,Security。說明 CVE-2021-44228 的影響、經由 JNDI 與 LDAP 的程式碼執行,以及修正版。 ↩
相關文章
共用相同標籤的最新文章。能以相近的主題延伸理解。
資訊處理安全確保支援士(情報処理安全確保支援士) 2023年秋季(令和5年)下午問1解說 ── 16則評論卻只顯示2則的儲存型XSS
以資訊處理安全確保支援士考試2023年秋季(令和5年)下午問1為題材,解說儲存型XSS的攻擊流程。整理輸入字數限制被分段投稿突破的原因、工作階段 ID 在未經外部通訊的情況下以圖片形式被取出的路徑,以及當中真正發揮作用的因應對策。
資訊處理安全確保支援士(情報処理安全確保支援士) 2023年秋季(令和5年) 下午問2解說 ── 從來賓用Wi-Fi被帶出的檔案
以資訊處理安全確保支援士考試 2023年秋季(令和5年) 下午問2為題材,解說堵住USB隨身碟的公司如何被人從來賓用Wi-Fi帶出檔案的路徑。整理伺服器憑證驗證與HSTS、MAC位址過濾的侷限性,以及EAP-TLS和TPM帶來的因應對策。
網站發包方也該了解 ── 把 IPA《安全的網站製作方式》當作檢查清單來用
公司網站的資訊安全該用什麼基準來確認?本文以發包方、營運方也能理解的說法,解說 IPA《安全的網站製作方式》所列出的 11 項漏洞與對策,並介紹在發包、驗收、營運各階段的用法。
資訊安全10大威脅 2026 ── 排行榜的正確解讀方式,以及中小企業真正該做的對策
IPA「資訊安全10大威脅2026」中,勒索軟體攻擊連續11年蟬聯第1名,供應鏈攻擊位居第2,首度入選的「AI使用相關的網路風險」則排名第3。本文將解說組織篇前10名的內容,以及中小企業應將哪些威脅視為自身課題並加以對策。
中小企業的資安對策,該從何開始 ── IPA「中小企業資訊安全對策指南」第4.0版導覽
中小企業的資安對策該從何開始?本文以IPA「中小企業資訊安全對策指南」第4.0版為基礎,從資訊安全六條款、5分鐘自我診斷,到SECURITY ACTION,依序分階段解說。
相關主題
與本文相近的主題頁面。以本文為起點,可進一步連到相關服務與其他文章。
Windows 技術主題
彙整 KomuraSoft LLC 關於 Windows 開發、故障調查與既有資產活用文章的主題中心。
與本主題相關的服務
本文連結到以下服務頁面,歡迎從最接近的入口查看。
網站製作
因為在會員 API 與智慧型手機的串接中,JWT 驗證、物件層級授權、可更新屬性的限制,都直接關係到 Web 系統的安全性。
技術諮詢 & 設計審查
因為透過設計審查找出既有 API 的授權缺口、相依函式庫的影響範圍與 WAF 暫定規則,屬於技術諮詢的範疇。
常見問題
整理諮詢這個主題時常見的問題。
- JWT 的簽章驗證明明通過了,為什麼還能冒充他人?
- 在本題中,JWT 管理函式庫依攻擊者指定的方式接受 JWT 標頭中的 alg,把 alg=none 的 JWT 當成沒有簽章卻仍屬正確的權杖來處理。因此,就算改寫酬載中的使用者 ID,驗證依然會通過。考試的解答是:以 JWT 標頭的 alg 為驗證對象,確認其值不是 NONE。不過實務上只拒絕 NONE 並不夠。應在伺服器端的設定中把允許使用的演算法固定為 RS256 等,不讓權杖自行申報的演算法直接用來決定選擇。此外還要依用途驗證 issuer、audience、有效期限、subject 等項目。
- 只要比對 JWT 內的使用者 ID 與請求中的 mid,作為授權對策就足夠了嗎?
- 作為本題的解答已經足夠。在共用模組 P 中驗證 JWT 所含的使用者 ID 與 mid 是否一致,就能擋下指定他人 mid 的攻擊。不過,如果 API 只處理使用者本人的資訊,實務上更安全的設計是不從用戶端接收 mid,而由已驗證 JWT 的 subject 決定使用者 ID。例如設計成 GET /users/me 或 PUT /users/me,就能從根本減少漏寫比對處理這類缺陷本身。管理者操作他人資料的 API,則要拆成另外的端點與授權原則。
- 追加 status 的攻擊,為什麼光靠一般的輸入值驗證擋不住?
- 因為就算驗證了 name 的長度或 age 的範圍,只要把原本就不該接收的 status 自動綁定並傳給內部物件,還是擋不住。問題不在於值的格式,而在於該屬性是否允許由使用者變更,也就是屬性層級的授權。更新用的輸入型別中只定義 name 與 age,未知的屬性一律拒絕。付費狀態則必須只依支付服務的成功結果這類伺服器可信任的事件來變更。
- 4 位數的認證碼 10 分鐘就會失效,為什麼還是很危險?
- 因為從 0000 到 9999 的候選只有 1 萬種,每秒能嘗試 10 次時,平均 5,000 次、也就是 500 秒就會命中。有效期間 10 分鐘等於 600 秒,依序嘗試不重複的候選,就能在有效期間內查驗 6,000 種。光靠失效時間擋不住暴力破解,必須把候選數量、嘗試速度與嘗試次數上限放在一起設計。
- 設問給的對策是鎖定帳戶,實務上只做即時鎖定就夠了嗎?
- 不夠。題目空格中要填的是「連續失敗次數超過門檻就鎖定帳戶的處理」,但如果只做固定式的永久鎖定,攻擊者就能刻意讓他人的帳戶被鎖定,造成服務妨害。實務上要組合帳戶單位的失敗次數、階段性的等待時間、對來源與裝置的風險判定、通知,以及復原程序。發出新的認證碼時不把失敗次數歸零,同樣重要。
- 把 WAF 設為偵測而不是阻斷,意義何在?
- 意義在於,就算把正常字串誤判為攻擊,也不會因此中斷業務通訊。題目的解答中,好處是可以避免因誤判而遭到阻斷,應執行的內容則是收到警示後詳查是否真的是攻擊。偵測模式不是放著不管的設定,而是用來確認日誌、排除誤判、調整規則,為轉入阻斷做準備的觀測期間。若已知的重大漏洞正遭到實際利用而情況緊急,也可能權衡可用性風險後,判斷先阻斷比較好。
- 本題中的函式庫 H 就是 Log4j 嗎?
- 題目原文隱去了產品名稱,不過 JNDI Lookup、LDAP 伺服器、從 HTTP 伺服器取得類別檔案、嵌入 HTTP 標頭的字串、CVSS v3.1 偏高的基本分數,這一整串攻擊流程,很自然地可以讀成對以 Log4Shell 之名為人所知的 CVE-2021-44228 所做的抽象化。本文會說明這層對應關係,但考試中不需要回答專有名稱,光憑題目給出的攻擊步驟與 WAF 規格就能作答。
- 從這道題目應該帶回實務的是什麼?
- 重點在於:認證通過、JWT 未遭竄改、可以存取目標物件、可以變更目標屬性,這些全都是各自獨立的確認。此外,短位數的認證碼需要嘗試次數限制,而面對重大的函式庫漏洞,影響確認、暫定防禦與根本修正要並行推進。把授權集中到共用元件、把輸入結構描述做成許可清單、在伺服器端固定 JWT 的驗證條件、掌握相依函式庫並保持隨時可更新的狀態,才是實務上的核心。