我在 HS256 與 RS256 之間做選擇時,不會只問「哪一個比較安全」,而會先問:誰需要簽 token?誰只需要驗證?金鑰會被分發到多少個系統? 這些問題通常比演算法名稱本身更能決定正確架構。
HS256 的 trust model
HS256 使用 HMAC SHA-256。issuer 與 verifier 共用同一個 secret。
Issuer ─┐
├─ shared secret → sign / verify
Verifier┘
優點是簡單:沒有 public/private key pair,也不需要 JWKS。
但它有一個很重要的特性:能驗證的人通常也擁有產生有效簽章的能力。 如果多個微服務都拿到同一個 secret,那些服務不只是 verifier,也具備 signing capability。
RS256 的 trust model
RS256 使用 RSA private key 簽章,public key 驗證。
Issuer
private key → sign
↓
JWT
↓
Verifier A/B/C
public key → verify
這讓我可以把 signing authority 留在 issuer,其他 API 只持有 public key。
當 verifier 數量增加、跨團隊或跨服務時,這種權限分離通常很有價值。
我實際比較的項目
| 項目 | HS256 | RS256 |
|---|---|---|
| 金鑰 | shared secret | private/public pair |
| 誰能簽 | 拿到 secret 的系統 | 持有 private key 的 issuer |
| 誰能驗 | 拿到 secret 的系統 | 任何拿到 public key 的 verifier |
| 多服務部署 | secret 會擴散 | public key 可安全分發 |
| JWKS | 通常不適用 | 很適合 |
| Rotation | secret 協調較麻煩 | kid + JWKS 容易管理 |
什麼情況我會考慮 HS256
- issuer 與 verifier 在同一個小型 trust boundary;
- verifier 數量少;
- secret distribution 可以嚴格控制;
- 不需要公開 verification;
- 架構簡單比 key separation 更重要。
例如單一 backend 自己簽、自己驗,HS256 可以非常合理。
但當我開始把同一個 secret 複製到很多服務,就會重新評估。
什麼情況我更傾向 RS256
- 一個 issuer 對多個 API 發 token;
- verifier 不應具有 signing capability;
- 身分提供者原生提供 JWKS;
- 需要頻繁 key rotation;
- 多 region、多團隊或外部 verifier 需要驗證。
這時 private key 可以限制在 issuer,其他系統只拿 public JWK。
我不讓 token 的 alg 決定 verifier policy
不管使用 HS256 還是 RS256,我都會在 server 端先設定 allowed algorithms。
allowed algorithms = ["RS256"]
token header 還沒被驗證,所以它不能自己說「這次改用 HS256」或「這次用 none」。
JWT Security Analyzer 可以用明確 allowlist 檢查 unexpected algorithm。
HS256 的 secret 品質很重要
如果 secret 很短、可猜或直接寫進 source code,演算法本身再標準也沒有用。我會使用高 entropy random secret,並放在適當 secret manager。
我也不會把 production HS256 secret 放進 browser JavaScript。前端是 public client,送到 client 的 secret 就不再能當成真正秘密。
RS256 的 private key 保護同樣重要
RS256 並不會自動安全。private key 如果外洩,攻擊者就可以產生有效 token。
我會限制 private key 的存取、使用 KMS/HSM 或受控 signing environment,並設計 emergency rotation/revocation 流程。
Key rotation 的差異
HS256 換 secret 時,issuer 與所有 verifier 必須協調新舊 secret。
RS256 則常透過 kid + JWKS:
old key → old kid
new key → new kid
JWKS = [old public JWK, new public JWK]
先 publish new public key,再開始用 new private key 簽,最後等 old token 的 lifetime 結束後移除 old key。
完整流程可看 JWT Key Rotation。
我驗證 RS256 JWT 的順序
- decode token;
- 讀
alg與kid; - 確認 expected issuer;
- 從 trusted issuer 取得 JWKS;
- 在 trusted set 裡用
kid找 candidate; - 檢查
kty、alg、use、key_ops; - 驗證 signature;
- 檢查
iss、aud、exp、nbf。
JWKS Validator 就是為這條流程設計。
我不把效能當第一個選擇條件
在一般 web API 中,資料庫、network 與 business logic 往往比一次 JWT verification 更可能是瓶頸。我不會因為 HS256 通常計算較簡單,就犧牲需要的 trust separation。
反過來,如果只有單一小型 backend,我也不會只是因為 RS256 看起來更「企業級」就增加不必要的 key infrastructure。
我的決策清單
- signer 與 verifier 是否在同一 trust boundary?
- verifier 是否應該擁有 signing capability?
- 有多少 verifier?
- key 要怎麼分發?
- rotation 時 old/new key 如何 overlap?
- 是否已有 OIDC/JWKS infrastructure?
- allowed algorithm 在哪裡強制?
- issuer/audience/time policy 在哪裡驗證?
我最後選 HS256 或 RS256,不是看哪個名字更流行,而是看哪個模型可以讓 key ownership、signing authority 與 verification responsibility 最清楚。