argon2、bcrypt、pbkdf2 は、いずれもパスワードを安全に保存するためのハッシュ化アルゴリズムを提供するライブラリです。これらは平文のパスワードを復元不可能な文字列に変換し、データベース漏洩時のリスクを低減します。argon2 は最新のアルゴリズムでメモリ耐性を持ち、bcrypt は長年の実績を持つ業界標準、pbkdf2 は Node.js 標準モジュールで追加インストールが不要という特徴があります。
在构建 Node.js 应用程序时,安全地存储用户密码是架构决策中的关键一环。argon2、bcrypt 和 pbkdf2 都旨在解决同一问题:将明文密码转换为不可逆的哈希值。然而,它们在算法原理、性能特征和安全性方面存在显著差异。让我们从实际工程角度深入比较。
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 密码,已经使用了二十多年。
// bcrypt: 配置盐 rounds
const saltRounds = 10;
const hash = await bcrypt.hash('password123', saltRounds);
pbkdf2 (Password Based Key Derivation Function 2) 是 NIST 推荐的标准。
// pbkdf2: Node.js 原生 crypto 模块
const crypto = require('crypto');
const hash = crypto.pbkdf2Sync('password123', salt, 100000, 64, 'sha512');
依赖管理是前端架构师需要考虑的实际问题,尤其是在容器化或无服务器环境中。
argon2 需要编译原生 C++ 代码。
# 可能需要安装构建依赖
npm install argon2
# 如果编译失败,可能需要:
# apt-get install -y build-essential python3
bcrypt 同样依赖原生模块。
bcryptjs),但性能较差,仅用于不支持原生模块的环境。npm install bcrypt
# 或者在浏览器/无原生环境使用
npm install bcryptjs
pbkdf2 是 Node.js crypto 模块的一部分。
npm install,减少了依赖树和潜在的安全漏洞。// 无需安装,直接 require
const crypto = require('crypto');
性能不仅影响用户体验(注册/登录延迟),还影响服务器成本。
argon2 在相同安全级别下通常比 bcrypt 更快。
// argon2: 验证速度快
const isMatch = await argon2.verify(hash, 'password123');
bcrypt 随着 round 数增加,计算时间呈指数增长。
// bcrypt: 验证
const isMatch = await bcrypt.compare('password123', hash);
pbkdf2 性能高度依赖于迭代次数和哈希算法。
// pbkdf2: 异步验证
crypto.pbkdf2('password', salt, iterations, keylen, digest, (err, derivedKey) => {
// 比较 derivedKey
});
argon2// 推荐配置
const options = {
type: argon2.argon2id,
memoryCost: 2 ** 16,
timeCost: 3,
parallelism: 2
};
bcrypt// 保持现有逻辑
if (await bcrypt.compare(input, storedHash)) {
// 登录成功
}
pbkdf2node_modules 体积和构建复杂性。利用 Node.js 内置功能即可满足基本安全需求。// 使用内置 crypto
const hash = crypto.pbkdf2Sync(password, salt, 100000, 64, 'sha512').toString('hex');
这些库主要用于服务器端环境。在浏览器中使用它们需要谨慎。
argon2 和 bcrypt 通常依赖 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 传输。客户端哈希不能防止中间人攻击,且如果哈希值泄露,攻击者可直接重放哈希值登录。
| 特性 | argon2 | bcrypt | pbkdf2 |
|---|---|---|---|
| 安全性 | ⭐⭐⭐⭐⭐ (最高) | ⭐⭐⭐⭐ (高) | ⭐⭐⭐ (中) |
| 抗 GPU 攻击 | ✅ 强 (内存硬) | ❌ 弱 | ❌ 弱 |
| 依赖 | 📦 外部包 + 原生编译 | 📦 外部包 + 原生编译 | 🛠️ Node.js 内置 |
| 配置复杂度 | 🔧 高 (内存/时间/并行) | 🔧 中 (rounds) | 🔧 中 (迭代/算法) |
| 适用场景 | 新项目、高安全需求 | 遗留系统、通用场景 | 轻量级、低依赖需求 |
argon2 是目前的技术首选 🏆。如果您的团队能够处理原生依赖的构建配置,它提供了面向未来的安全性。特别是 argon2id 变体,结合了 argon2i 和 argon2d 的优点,是 OWASP 推荐的默认选择。
bcrypt 是稳妥的备选方案 🛡️。如果您担心原生编译的复杂性,或者需要与现有生态系统无缝集成,它仍然是安全的。但在新项目中,除非有特定限制,否则优先考虑 Argon2。
pbkdf2 仅在特定约束下使用 ⚠️。虽然它内置于 Node.js,但其安全性已不如前两者。仅在没有外部依赖允许、且风险可控的内部工具中使用。
最终思考:密码哈希只是安全链条中的一环。无论选择哪个库,都必须配合 HTTPS、适当的盐值生成(这些库通常自动处理)以及速率限制等措施,才能构建真正的防御体系。
最新のセキュリティ基準を最優先する場合に選択します。メモリハードな設計により GPU 攻撃に強く、Password Hashing Competition の勝者です。ネイティブバインディングのビルド環境が整っているサーバーサイドプロジェクトに最適です。
既存システムとの互換性や、広範なコミュニティサポートを重視する場合に選択します。多くのレガシーシステムで採用されており、ドキュメントや事例が豊富です。セキュリティ要件が極めて高い最新プロジェクトでは Argon2 に譲りますが、依然として安全な選択肢です。
外部依存を増やしたくない場合や、Node.js 標準機能のみで完結させたい場合に選択します。追加パッケージのインストールが不要ですが、現代の攻撃手法に対しては Argon2 や bcrypt よりも防御力が劣るため、新規プロジェクトでの採用は慎重に検討すべきです。
Bindings to the reference Argon2 implementation.
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
}
See this article on the wiki for steps on how to migrate your existing code to Argon2. It's easy!
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(..);
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:
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.
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.
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.
$ 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
You can do either of the two methods below:
$ npm install argon2 --build-from-source
node-argon2 install script and build manually.$ npm install argon2 --ignore-scripts
$ npx node-gyp rebuild -C ./node_modules/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.
This project exists thanks to all the people who contribute. [Contribute].
Become a financial contributor and help us sustain our community. [Contribute]
Support this project with your organization. Your logo will show up here with a link to your website. [Contribute]
Work licensed under the MIT License. Please check P-H-C/phc-winner-argon2 for license over Argon2 and the reference implementation.