nodejs w / crypto中的SALT和HASH密码
我想弄清楚如何使用crypto模块在nodejs中encryption和散列密码。 我能够创build哈希密码做到这一点:
UserSchema.pre('save', function(next) { var user = this; var salt = crypto.randomBytes(128).toString('base64'); crypto.pbkdf2(user.password, salt, 10000, 512, function(err, derivedKey) { user.password = derivedKey; next(); }); });
不过,我很困惑如何以后validation密码。
UserSchema.methods.validPassword = function(password) { // need to salt and hash this password I think to compare // how to I get the salt? }
在你使用的任何持久性机制(数据库)中,你会把得到的散列和salt以及迭代次数一起存储,这两者都是纯文本。 如果每个密码都有不同的盐分(你应该这样做),你必须保存这些信息。
然后,您将比较新的纯文本密码,使用相同salt(和迭代)的散列,然后将字节序列与存储的字符序列进行比较。
要生成密码(伪)
function hashPassword(password) { var salt = crypto.randomBytes(128).toString('base64'); var iterations = 10000; var hash = pbkdf2(password, salt, iterations); return { salt: salt, hash: hash, iterations: iterations }; }
validation密码(伪)
function isPasswordCorrect(savedHash, savedSalt, savedIterations, passwordAttempt) { return savedHash == pbkdf2(passwordAttempt, savedSalt, savedIterations); }
基于nodejs文档( http://nodejs.org/api/crypto.html ),它看起来不像是有一个特定的方法来validation你的密码。 要手动validation它,你将需要计算当前提供的密码的哈希值,并将其与存储的密码进行比较以求相等。 基本上,你可以用原来的挑战密码来做同样的事情,但是使用存储在数据库中的盐而不是生成一个新的密码,然后比较两个哈希值。
如果您不太愿意使用内置的encryption库,我可能会推荐使用bcrypt 。 两者在安全方面大致相同,但我认为bcrypt具有更友好的用户界面。 如何使用它的一个例子(直接从上面链接的页面上的bcrypt文档中获取)是这样的:
创build一个哈希:
var bcrypt = require('bcrypt'); var salt = bcrypt.genSaltSync(10); var hash = bcrypt.hashSync("B4c0/\/", salt); // Store hash in your password DB.
检查密码:
// Load hash from your password DB. bcrypt.compareSync("B4c0/\/", hash); // true bcrypt.compareSync("not_bacon", hash); // false
编辑添加:
bcrypt的另一个优点是genSalt函数的输出在一个string中同时包含散列和salt 。 这意味着您可以只将单个项目存储在数据库中,而不是两个。 还提供了一种方法,在散列发生的同时产生一个盐,所以你根本不用担心盐的pipe理。
编辑更新:
为了回应Peter Lyons的评论:你100%正确。 我曾经假设,我推荐的bcrypt模块是一个javascript实现,因此asynchronous使用它并不会真正加速节点的单线程模型。 事实certificate情况并非如此; bcrypt模块使用本地c ++代码进行计算,并且asynchronous运行速度更快。 Peter Lyons是对的,您应该先使用该方法的asynchronous版本,并在必要时仅select同步版本。 asynchronous方法可能与同步方法一样慢,但同步方法总是慢的。
将密码和salt存储在数据库的不同列中,或者(我的首选方法),将密码以与RFC 2307第5.3节兼容的格式存储在数据库中。 一个例子是{X-PBKDF2}base64salt:base64digest
。 您也可以将迭代次数存储在那里,这样您就可以为未更新密码的新帐户和帐户增加迭代次数,而不会中断其他人的login。
来自我自己的Perl的PBKDF2模块的示例哈希看起来像
{X-PBKDF2}HMACSHA1:AAAD6A:8ODUPA==:1HSdSVVwlWSZhbPGO7GIZ4iUbrk=
它包括使用的特定的散列algorithm,以及迭代次数,盐和结果键。
我认为这个教程对你来说最适合。 只要通过它,它是我find的最好的。 护照教程与Node.js和encryption
希望你觉得有帮助。
面对同样的问题,我把所有东西放在一个模块中: https : //www.npmjs.org/package/password-hash-and-salt
它使用pbkdf2并将散列,盐,algorithm和迭代存储在单个字段中。 希望能帮助到你。
在这种情况下涉及两个主要步骤
1)创build和存储密码
在这里你将不得不做以下的事情。
- 取用户密码
- 生成一串随机字符(盐)
- 将salt与用户input的密码组合在一起
- 散列组合的string。
- 将散列和盐存储在数据库中。
2)validation用户密码
这一步将需要validation用户。
-
用户将input用户名/电子邮件和密码。
-
根据input的用户名获取散列和盐
-
将salt与用户密码组合在一起
-
使用相同的哈希algorithm哈希组合。
-
比较结果。
本教程详细解释了如何使用nodejs crypto完成此操作。 正是你在找什么。 使用NodeJSencryption的盐哈希密码
这是@Matthews答案的修改版本,使用TypeScript
import * as crypto from 'crypto'; const PASSWORD_LENGTH = 256; const SALT_LENGTH = 64; const ITERATIONS = 10000; const DIGEST = 'sha256'; const BYTE_TO_STRING_ENCODING = 'hex'; // this could be base64, for instance /** * The information about the password that is stored in the database */ interface PersistedPassword { salt: string; hash: string; iterations: number; } /** * Generates a PersistedPassword given the password provided by the user. This should be called when creating a user * or redefining the password */ export async function generateHashPassword(password: string): Promise<PersistedPassword> { return new Promise<PersistedPassword>((accept, reject) => { const salt = crypto.randomBytes(SALT_LENGTH).toString(BYTE_TO_STRING_ENCODING); crypto.pbkdf2(password, salt, ITERATIONS, PASSWORD_LENGTH, DIGEST, (error, hash) => { if (error) { reject(error); } else { accept({ salt, hash: hash.toString(BYTE_TO_STRING_ENCODING), iterations: ITERATIONS, }); } }); }); } /** * Verifies the attempted password against the password information saved in the database. This should be called when * the user tries to log in. */ export async function verifyPassword(persistedPassword: PersistedPassword, passwordAttempt: string): Promise<boolean> { return new Promise<boolean>((accept, reject) => { crypto.pbkdf2(passwordAttempt, persistedPassword.salt, persistedPassword.iterations, PASSWORD_LENGTH, DIGEST, (error, hash) => { if (error) { reject(error); } else { accept(persistedPassword.hash === hash.toString(BYTE_TO_STRING_ENCODING)); } }); }); }
- Node.js'crypto.createSign()的有效algorithminputstring
- 节点JSencryption,不能创build带有重音的字符的hmac
- 使用SHA-256和NodeJSencryption
- crypto#randomBytes是如何随机的?
- node.jsencryption模块不能encryption16个以上的字符
- 如何在NodeJS中使用aes-256-ecb正确encryptionstring并使用OpenSSL的命令行解密?
- 在nodejs中创buildRSA令牌
- 检查zip或gzip是否是密码encryption的(Node.JS)
- 用python和nodejsencryption和解密