los*_*ion 30 node.js node-crypto
我试图找出如何使用crypto模块在nodejs中加密和散列密码.我可以创建哈希密码来做到这一点:
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();
});
});
Run Code Online (Sandbox Code Playgroud)
但是我对如何验证密码感到困惑.
UserSchema.methods.validPassword = function(password) {
// need to salt and hash this password I think to compare
// how to I get the salt?
}
Run Code Online (Sandbox Code Playgroud)
Mat*_*hew 44
在您正在使用的任何持久性机制(数据库)中,您将存储结果哈希以及盐和迭代次数,这两者都是纯文本.如果每个密码使用不同的盐(您应该这样做),您还必须保存该信息.
然后,您将比较新的纯文本密码,使用相同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
};
}
Run Code Online (Sandbox Code Playgroud)
验证密码(伪)
function isPasswordCorrect(savedHash, savedSalt, savedIterations, passwordAttempt) {
return savedHash == pbkdf2(passwordAttempt, savedSalt, savedIterations);
}
Run Code Online (Sandbox Code Playgroud)
Twe*_*les 24
基于nodejs文档(http://nodejs.org/api/crypto.html),看起来没有一种特定的方法可以为您验证密码.要手动验证它,您需要计算当前提供的密码的哈希值,并将其与存储的密码进行比较以获得相等性.基本上,您将使用您对原始密码执行的质询密码执行相同的操作,但使用存储在数据库中的salt而不是生成新的密码,然后比较两个哈希值.
如果您不太愿意使用内置的加密库,我可能会建议使用bcrypt.这两个在安全方面大致相同,但我认为bcrypt具有更加用户友好的界面.如何使用它的一个示例(直接从上面链接的页面上的bcrypt文档中获取)将是这样的:
创建哈希:
var bcrypt = require('bcrypt');
var salt = bcrypt.genSaltSync(10);
var hash = bcrypt.hashSync("B4c0/\/", salt);
// Store hash in your password DB.
Run Code Online (Sandbox Code Playgroud)
要检查密码:
// Load hash from your password DB.
bcrypt.compareSync("B4c0/\/", hash); // true
bcrypt.compareSync("not_bacon", hash); // false
Run Code Online (Sandbox Code Playgroud)
编辑添加:
bcrypt的另一个优点是genSalt函数的输出包含一个字符串中的散列和salt.这意味着您可以只存储数据库中的单个项目,而不是两个.还提供了一种方法,可以在散列发生的同时生成盐,因此您根本不必担心管理盐.
编辑更新:
回应Peter Lyons的评论:你是100%正确的.我曾经假设我推荐的bcrypt模块是一个javascript实现,因此异步使用它不会真正加速节点的单线程模型.事实证明事实并非如此; bcrypt模块使用本机c ++代码进行计算,并且异步运行速度更快.Peter Lyons是对的,您应该首先使用该方法的异步版本,并在必要时仅选择同步版本.异步方法可能与同步方法一样慢,但同步方法总是很慢.
这是@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));
}
});
});
}
Run Code Online (Sandbox Code Playgroud)
小智 7
面对同样的问题,我将所有内容整合到一个模块中:https://www.npmjs.org/package/password-hash-and-salt
它使用pbkdf2并在单个字段中存储散列,salt,算法和迭代.希望能帮助到你.
| 归档时间: |
|
| 查看次数: |
53107 次 |
| 最近记录: |