Contents

API 防重放小記(replay attack)

最近在想 API 傳輸安全時,順手重新整理了一次 replay attack。這個主題很常被一句「做簽章就好」帶過,但實際上如果只做簽章、不做時效與唯一性控制,重放攻擊還是擋不住。

什麼是 replay attack

Replay attack 中文常翻成重放攻擊。意思是攻擊者不一定要看懂內容,也不一定要竄改內容,只要把一個原本合法的請求原封不動重送一次,就可能造成問題。

例如:

  1. 扣款 API 被重送一次,重複扣款。
  2. 領券 API 被重送多次,重複領取。
  3. 開門、解鎖、下單這類操作被重播。

也就是說,資料內容沒被改,系統還是可能中招。

只做簽章為什麼不夠

很多人第一反應是做 sign 簽章。簽章確實能防止內容被竄改,但如果攻擊者拿到一包「原本就合法」的請求,連同 body、timestamp 以外的所有內容一起重送,server 還是可能驗證通過。

所以防重放至少要同時處理三件事:

  1. 請求內容沒有被改。
  2. 請求有時效。
  3. 請求只能被接受一次。

常見做法

1. 簽章

客戶端把重要欄位組成固定字串,再用共享密鑰或私鑰做簽章。Server 收到後用同樣規則驗證,確認內容沒被改。

2. Timestamp

請求帶上時間戳記,例如只接受 5 分鐘內的請求。這樣就算封包被攔到,也不能無限期重送。

3. Nonce

每次請求額外帶一個隨機唯一值。Server 驗證成功後,把這個 nonce 記錄下來,短時間內如果又收到一模一樣的 nonce,就拒絕。

這一步通常才是真正防 replay 的關鍵。

4. Token / 身分驗證

如果不是完全開放的 API,搭配 token 或登入機制可以再把攻擊面縮小。至少不是任何人攔到封包就能隨便打。

一個簡化流程

我自己比較容易記的流程是:

  1. 客戶端準備 path + body + timestamp + nonce
  2. 用共享密鑰算出簽章。
  3. Server 驗證簽章是否正確。
  4. Server 檢查 timestamp 是否過期。
  5. Server 檢查 nonce 是否已經用過。
  6. 全部通過才真的執行業務邏輯。

只少其中一層,效果都會差很多。

我當時想到的疑問

我那時也有想到一個很實際的問題:如果 App 被逆向,密鑰被挖出來,不就還是無解?

這個問題其實沒錯。所以這類防護很多時候是在防:

  1. 中間人攔包重送。
  2. 一般腳本重放。
  3. 低成本濫用。

它不是在保證「App 被完整破解後仍百分之百安全」。那又是另一個層級的攻防問題。

我的結論

防重放不是單一功能,而是一組策略。只做 sign 比只上 HTTPS 好,但還不夠完整。比較實際的組合通常是:HTTPS + 簽章 + timestamp + nonce + 身分驗證,再搭配 server 端真正的冪等設計。

參考