結論先講

如果你現在要選,選 Argon2id。 它是 OWASP 2024 推薦的首選,同時防禦 CPU 暴力破解和 GPU/ASIC 攻擊。bcrypt 不是不安全,但它只防 CPU 攻擊——2026 年的 GPU 每秒能算的 bcrypt hash 數量已經很可觀了。

不過我們壓測用 bcrypt rounds=10 是有原因的——各語言的 Argon2 實作差異太大,不適合當跨框架比較的控制變因。


三種算法一句話說明

算法發明年份一句話防禦對象
bcrypt1999刻意用 CPU 時間讓暴力破解變慢CPU 暴力破解
scrypt2009除了 CPU,還吃大量記憶體CPU + GPU 暴力破解
Argon22015CPU + 記憶體 + 可選的平行度CPU + GPU + ASIC

為什麼「吃記憶體」很重要

bcrypt 只用 CPU。現代 GPU 有幾千個核心,每個核心都能獨立算 bcrypt——一張 RTX 4090 每秒能算幾千次 bcrypt。

但 GPU 的記憶體是共享的。如果每次 hash 需要 64MB 記憶體,一張 24GB 的 GPU 最多同時算 375 次——效率比 bcrypt 低好幾個數量級。

scrypt 和 Argon2 就是利用這個原理。 它們故意佔用大量記憶體,讓 GPU 攻擊變得不划算。


Argon2 的三種變體

Argon2 有三個版本:

變體特性適用場景
Argon2d防 GPU,但可能受 side-channel attack加密貨幣
Argon2i防 side-channel,但對 GPU 防禦較弱密碼 hash
Argon2id混合 d 和 i 的優點推薦用這個

OWASP 推薦的參數(2024):

Argon2id
- memory: 19456 KB (19 MB)
- iterations: 2
- parallelism: 1

效能比較

在同等安全強度下,三者的效能:

算法參數單次 Hash記憶體使用
bcryptrounds=10~100ms~4 KB
scryptN=2^15, r=8, p=1~100ms~32 MB
Argon2idm=19456, t=2, p=1~100ms~19 MB

三者的 CPU 時間差不多(都可以調到 ~100ms),差異在記憶體:

  • bcrypt 幾乎不用額外記憶體(4 KB)
  • scrypt 用 32 MB
  • Argon2id 用 19 MB

記憶體對壓測的影響

假設 100 個併發 request 都在做 hash:

  • bcrypt:100 × 4KB = 400KB(忽略不計)
  • Argon2id:100 × 19MB = 1.9 GB

在我們 2 GB Docker 限制的壓測環境中,Argon2id 在 100 併發時就會吃掉幾乎所有記憶體。這就是為什麼我們壓測用 bcrypt 而非 Argon2——記憶體的變因太大,會干擾框架本身的比較。


各語言的 Argon2 支援

語言推薦 Library成熟度
Node.jsargon2 (native binding)成熟
Pythonargon2-cffi成熟
Gogolang.org/x/crypto/argon2成熟
Javade.mkammerer.argon2成熟
C#Isopoh.Cryptography.Argon2中等
PHPpassword_hash(PASSWORD_ARGON2ID)原生支援(PHP 7.3+)

PHP 是唯一在標準庫就支援 Argon2id 的——password_hash($password, PASSWORD_ARGON2ID) 就搞定了。


那我應該換掉 bcrypt 嗎?

換的理由

  1. 你的威脅模型包含 GPU 攻擊: 如果攻擊者可能用 GPU 叢集破解你的密碼 hash,bcrypt 的防禦力在 2026 年已經不夠
  2. OWASP 推薦: 2024 指南把 Argon2id 列為首選
  3. 新專案沒有歷史包袱: 直接用 Argon2id 開始

不換的理由

  1. 現有系統有幾百萬筆 bcrypt hash: 遷移需要在用戶登入時逐步 rehash
  2. bcrypt 不是「不安全」: rounds=10 在 2026 年仍然是可接受的安全等級
  3. Argon2 的記憶體需求更高: 在記憶體受限的環境(容器、serverless)可能是問題
  4. 穩定性: bcrypt 27 年的歷史,各語言實作都很穩定。Argon2 的某些語言 library 還在積極開發

折中方案

新用戶 → Argon2id
舊用戶 → 下次登入時 rehash 成 Argon2id
過渡期 → 兩種都支援

大部分框架的 auth library 都支援這種漸進式遷移。


回到壓測的脈絡

我們選 bcrypt rounds=10 作為壓測的控制變因,是因為:

  1. 統一性: 所有語言都有成熟的 bcrypt library
  2. 可預測性: bcrypt 的記憶體使用可忽略,不會干擾框架比較
  3. 代表性: 大部分現有系統仍在用 bcrypt,測試結果對這些系統有參考價值

如果我們用 Argon2id,測出來的結果會受「各框架的記憶體管理效率」影響——Go 的 memory allocator vs JVM GC vs Python GC。這雖然也是有趣的比較,但會把焦點從「框架效能」移到「記憶體管理」,不是這個系列的主題。


下一篇

CRUD 壓測總覽:9 框架排名與場景設計 — Bcrypt 的背景講完了。接下來進入第一場實戰——9 個框架在最基礎的 User CRUD 場景下,誰活得最久。


本系列文章

完整 68 篇目錄見 系列首頁

← 上一篇:各語言 bcrypt 實作差異:同一個算法,效能差一倍 → 下一篇:CRUD 壓測總覽:9 框架排名與共同天花板