argon2 vs bcrypt vs pbkdf2
Node.js アプリケーションにおけるパスワードハッシュ化ライブラリの比較
argon2bcryptpbkdf2類似パッケージ:

Node.js アプリケーションにおけるパスワードハッシュ化ライブラリの比較

argon2bcryptpbkdf2 は、いずれもパスワードを安全に保存するためのハッシュ化アルゴリズムを提供するライブラリです。これらは平文のパスワードを復元不可能な文字列に変換し、データベース漏洩時のリスクを低減します。argon2 は最新のアルゴリズムでメモリ耐性を持ち、bcrypt は長年の実績を持つ業界標準、pbkdf2 は Node.js 標準モジュールで追加インストールが不要という特徴があります。

npmのダウンロードトレンド

3 年

GitHub Starsランキング

統計詳細

パッケージ
ダウンロード数
Stars
サイズ
Issues
公開日時
ライセンス
argon202,1511.03 MB510ヶ月前MIT
bcrypt07,8011.11 MB341年前MIT
pbkdf20202-311日前MIT

密码哈希库深度对比:Argon2 vs Bcrypt vs PBKDF2

在构建 Node.js 应用程序时,安全地存储用户密码是架构决策中的关键一环。argon2bcryptpbkdf2 都旨在解决同一问题:将明文密码转换为不可逆的哈希值。然而,它们在算法原理、性能特征和安全性方面存在显著差异。让我们从实际工程角度深入比较。

🔐 算法原理与安全性

argon2 是 2015 年密码哈希竞赛的获胜者,专为抵抗 GPU 和 ASIC 攻击而设计。

  • 它不仅是计算密集型,还是内存密集型。
  • 攻击者需要大量内存才能并行破解,这大大增加了硬件成本。
// argon2: 配置内存和并行度
const hash = await argon2.hash('password123', {
  type: argon2.argon2id,
  memoryCost: 65536, // 64MB
  timeCost: 3,       // 3 次迭代
  parallelism: 4     // 4 个线程
});

bcrypt 基于 Blowfish 密码,已经使用了二十多年。

  • 它是计算密集型的,但对内存的要求不高。
  • 随着 GPU 性能的提升,其抵抗暴力破解的能力相对减弱,但仍被广泛认为是安全的。
// bcrypt: 配置盐 rounds
const saltRounds = 10;
const hash = await bcrypt.hash('password123', saltRounds);

pbkdf2 (Password Based Key Derivation Function 2) 是 NIST 推荐的标准。

  • 它通常与 HMAC-SHA256 结合使用。
  • 它是计算密集型的,但缺乏内存硬度,容易受到大规模并行硬件攻击。
// pbkdf2: Node.js 原生 crypto 模块
const crypto = require('crypto');
const hash = crypto.pbkdf2Sync('password123', salt, 100000, 64, 'sha512');

⚙️ 安装与依赖管理

依赖管理是前端架构师需要考虑的实际问题,尤其是在容器化或无服务器环境中。

argon2 需要编译原生 C++ 代码。

  • 在安装时需要 Python 和 C++ 构建工具链。
  • 在某些无服务器平台或精简的 Docker 镜像中可能需要额外配置。
# 可能需要安装构建依赖
npm install argon2
# 如果编译失败,可能需要:
# apt-get install -y build-essential python3

bcrypt 同样依赖原生模块。

  • 它也有预编译的二进制文件,但在架构不匹配时仍需编译。
  • 社区中有纯 JS 实现(如 bcryptjs),但性能较差,仅用于不支持原生模块的环境。
npm install bcrypt
# 或者在浏览器/无原生环境使用
npm install bcryptjs

pbkdf2 是 Node.js crypto 模块的一部分。

  • 无需 npm install,减少了依赖树和潜在的安全漏洞。
  • 对于追求最小化依赖的项目非常有吸引力。
// 无需安装,直接 require
const crypto = require('crypto');

⚡ 性能与资源消耗

性能不仅影响用户体验(注册/登录延迟),还影响服务器成本。

argon2 在相同安全级别下通常比 bcrypt 更快。

  • 由于内存硬度,它在普通 CPU 上效率高,但在专用破解硬件上效率极低。
  • 可以通过调整参数灵活平衡时间和内存。
// argon2: 验证速度快
const isMatch = await argon2.verify(hash, 'password123');

bcrypt 随着 round 数增加,计算时间呈指数增长。

  • 为了保持安全性,需要不断增加 round 数,这会逐渐增加登录延迟。
  • 在低端服务器上可能导致明显的请求阻塞。
// bcrypt: 验证
const isMatch = await bcrypt.compare('password123', hash);

pbkdf2 性能高度依赖于迭代次数和哈希算法。

  • 使用 SHA-512 比 SHA-1 更安全但稍慢。
  • 由于缺乏内存硬度,攻击者可以用低成本硬件进行大规模并行破解。
// pbkdf2: 异步验证
crypto.pbkdf2('password', salt, iterations, keylen, digest, (err, derivedKey) => {
  // 比较 derivedKey
});

🌐 实际应用场景

场景 1:新建高安全要求的 SaaS 平台

  • 最佳选择: argon2
  • 理由:作为新系统,没有历史包袱。应直接采用当前最安全的标准,保护用户数据免受未来硬件进步带来的威胁。
// 推荐配置
const options = {
  type: argon2.argon2id,
  memoryCost: 2 ** 16,
  timeCost: 3,
  parallelism: 2
};

场景 2:维护现有的大型用户系统

  • 最佳选择: bcrypt
  • 理由:数据库中已存储了数百万个 bcrypt 哈希。迁移成本过高且风险大。继续使用该库保持兼容性,并在用户重新登录时逐步迁移到更强算法(如果需要)。
// 保持现有逻辑
if (await bcrypt.compare(input, storedHash)) {
  // 登录成功
}

场景 3:轻量级工具或内部管理系统

  • 最佳选择: pbkdf2
  • 理由:风险相对较低,且希望减少 node_modules 体积和构建复杂性。利用 Node.js 内置功能即可满足基本安全需求。
// 使用内置 crypto
const hash = crypto.pbkdf2Sync(password, salt, 100000, 64, 'sha512').toString('hex');

⚠️ 注意事项与限制

这些库主要用于服务器端环境。在浏览器中使用它们需要谨慎。

  • argon2bcrypt 通常依赖 Node.js 原生模块,无法直接在浏览器运行。如果在浏览器端进行哈希(不推荐,因为会暴露哈希值作为密码等价物),需要使用 WebAssembly 版本或纯 JS 实现,性能会大幅下降。
  • pbkdf2 在浏览器中可以通过 Web Crypto API 实现,但 API 略有不同。
// 浏览器端 Web Crypto API (类似 pbkdf2)
const keyMaterial = await crypto.subtle.importKey(...);
const key = await crypto.subtle.deriveKey(...);

💡 提示:永远不要在客户端单独进行密码哈希来替代 HTTPS 传输。客户端哈希不能防止中间人攻击,且如果哈希值泄露,攻击者可直接重放哈希值登录。

📌 总结对比表

特性argon2bcryptpbkdf2
安全性⭐⭐⭐⭐⭐ (最高)⭐⭐⭐⭐ (高)⭐⭐⭐ (中)
抗 GPU 攻击✅ 强 (内存硬)❌ 弱❌ 弱
依赖📦 外部包 + 原生编译📦 外部包 + 原生编译🛠️ Node.js 内置
配置复杂度🔧 高 (内存/时间/并行)🔧 中 (rounds)🔧 中 (迭代/算法)
适用场景新项目、高安全需求遗留系统、通用场景轻量级、低依赖需求

💡 架构师建议

argon2 是目前的技术首选 🏆。如果您的团队能够处理原生依赖的构建配置,它提供了面向未来的安全性。特别是 argon2id 变体,结合了 argon2iargon2d 的优点,是 OWASP 推荐的默认选择。

bcrypt 是稳妥的备选方案 🛡️。如果您担心原生编译的复杂性,或者需要与现有生态系统无缝集成,它仍然是安全的。但在新项目中,除非有特定限制,否则优先考虑 Argon2。

pbkdf2 仅在特定约束下使用 ⚠️。虽然它内置于 Node.js,但其安全性已不如前两者。仅在没有外部依赖允许、且风险可控的内部工具中使用。

最终思考:密码哈希只是安全链条中的一环。无论选择哪个库,都必须配合 HTTPS、适当的盐值生成(这些库通常自动处理)以及速率限制等措施,才能构建真正的防御体系。

選び方: argon2 vs bcrypt vs pbkdf2

  • argon2:

    最新のセキュリティ基準を最優先する場合に選択します。メモリハードな設計により GPU 攻撃に強く、Password Hashing Competition の勝者です。ネイティブバインディングのビルド環境が整っているサーバーサイドプロジェクトに最適です。

  • bcrypt:

    既存システムとの互換性や、広範なコミュニティサポートを重視する場合に選択します。多くのレガシーシステムで採用されており、ドキュメントや事例が豊富です。セキュリティ要件が極めて高い最新プロジェクトでは Argon2 に譲りますが、依然として安全な選択肢です。

  • pbkdf2:

    外部依存を増やしたくない場合や、Node.js 標準機能のみで完結させたい場合に選択します。追加パッケージのインストールが不要ですが、現代の攻撃手法に対しては Argon2 や bcrypt よりも防御力が劣るため、新規プロジェクトでの採用は慎重に検討すべきです。

argon2 のREADME

node-argon2

Financial contributors on Open Collective Build status NPM package

Bindings to the reference Argon2 implementation.

Usage

It's possible to hash using either Argon2i, Argon2d or Argon2id (default), and verify if a password matches a hash.

To hash a password:

const argon2 = require('argon2');

try {
  const hash = await argon2.hash("password");
} catch (err) {
  //...
}

To see how you can modify the output (hash length, encoding) and parameters (time cost, memory cost and parallelism), read the wiki

To verify a password:

try {
  if (await argon2.verify("<big long hash>", "password")) {
    // password match
  } else {
    // password did not match
  }
} catch (err) {
  // internal failure
}

Migrating from another hash function

See this article on the wiki for steps on how to migrate your existing code to Argon2. It's easy!

TypeScript usage

A TypeScript type declaration file is published with this module. If you are using TypeScript 2.0.0 or later, that means you do not need to install any additional typings in order to get access to the strongly typed interface. Simply use the library as mentioned above.

import * as argon2 from "argon2";

const hash = await argon2.hash(..);

Prebuilt binaries

node-argon2 provides prebuilt binaries from v0.26.0 onwards. They are built every release using GitHub Actions.

The current prebuilt binaries are built and tested with the following systems:

  • Ubuntu 22.04 (x86-64; ARM64 from v0.28.2; ARMv7 from v0.43.0)
  • MacOS 13 (x86-64)
  • MacOS 14 (ARM64 from v0.29.0)
  • Windows Server 2022 (x86-64)
  • Alpine Linux 3.18 (x86-64 from v0.28.1; ARM64 from v0.28.2; ARMv7 from v0.43.0)
  • FreeBSD 14 (x86-64 from v0.29.1; ARM64 from v0.44.0)

Binaries should also work for any version more recent than the ones listed above. For example, the binary for Ubuntu 20.04 also works on Ubuntu 22.04, or any other Linux system that ships a newer version of glibc; the binary for MacOS 11 also works on MacOS 12. If your platform is below the above requirements, you can follow the Before installing section below to manually compile from source. It is also always recommended to build from source to ensure consistency of the compiled module.

Before installing

You can skip this section if the prebuilt binaries work for you.

You MUST have a node-gyp global install before proceeding with the install, along with GCC >= 5 / Clang >= 3.3. On Windows, you must compile under Visual Studio 2015 or newer.

node-argon2 works only and is tested against Node >=18.0.0.

OSX

To install GCC >= 5 on OSX, use homebrew:

$ brew install gcc

Once you've got GCC installed and ready to run, you then need to install node-gyp, you must do this globally:

$ npm install -g node-gyp

Finally, once node-gyp is installed and ready to go, you can install this library, specifying the GCC or Clang binary to use:

$ CXX=g++-12 npm install argon2

NOTE: If your GCC or Clang binary is named something different than g++-12, you'll need to specify that in the command.

FAQ

How do I manually rebuild the binaries?
$ npx @mapbox/node-pre-gyp rebuild -C ./node_modules/argon2

Run @mapbox/node-pre-gyp instead of node-gyp because node-argon2's binding.gyp file relies on variables from @mapbox/node-pre-gyp.

You can omit npx @mapbox and use just node-pre-gyp if you have a global installation of @mapbox/node-pre-gyp, otherwise prefixing npx will use the local one in ./node_modules/.bin

How do I skip installing prebuilt binaries and manually compile from source?

You can do either of the two methods below:

  1. Force build from source on install.
$ npm install argon2 --build-from-source
  1. Ignore node-argon2 install script and build manually.
$ npm install argon2 --ignore-scripts
$ npx node-gyp rebuild -C ./node_modules/argon2
I installed Node as a snap, and I can't install node-argon2.

This seems to be an issue related to snap (see #345 (comment)). Installing Node with another package manager, such as asdf or nvm, is a possible workaround.

Contributors

Code contributors

This project exists thanks to all the people who contribute. [Contribute].

Financial contributors

Become a financial contributor and help us sustain our community. [Contribute]

Individuals

Organizations

Support this project with your organization. Your logo will show up here with a link to your website. [Contribute]

License

Work licensed under the MIT License. Please check P-H-C/phc-winner-argon2 for license over Argon2 and the reference implementation.