import { scrypt, randomBytes, timingSafeEqual } from "crypto";
import { promisify } from "util";
// scrypt is callback based so with promisify we can await it
const scryptAsync = promisify(scrypt);
哈希过程有两种方法。第一种方法是将密码进行哈希,第二种方法是需要将新的登录密码与存储的密码进行比较。我使用TypeScript详细编写了所有内容。
export class Password {
static async hashPassword(password: string) {
const salt = randomBytes(16).toString("hex");
const buf = (await scryptAsync(password, salt, 64)) as Buffer;
return `${buf.toString("hex")}.${salt}`;
}
static async comparePassword(
storedPassword: string,
suppliedPassword: string
): Promise<boolean> {
// split() returns array
const [hashedPassword, salt] = storedPassword.split(".");
// we need to pass buffer values to timingSafeEqual
const hashedPasswordBuf = Buffer.from(hashedPassword, "hex");
// we hash the new sign-in password
const suppliedPasswordBuf = (await scryptAsync(suppliedPassword, salt, 64)) as Buffer;
// compare the new supplied password with the stored hashed password
return timingSafeEqual(hashedPasswordBuf, suppliedPasswordBuf);
}
}
测试一下:
Password.hashPassword("123dafdas")
.then((res) => Password.comparePassword(res, "123edafdas"))
.then((res) => console.log(res));
// add new user
app.get('/newUser', (req, res) => {
let username = req.query.username || '';
const password = req.query.password || '';
username = username.replace(/[!@#$%^&*]/g, '');
if (!username || !password || users[username]) {
return res.sendStatus(400);
}
const salt = crypto.randomBytes(128).toString('base64');
const hash = crypto.pbkdf2Sync(password, salt, 10000, 512, 'sha512');
users[username] = { salt, hash };
res.sendStatus(200);
});
// validating user authentication attempts
app.get('/auth', (req, res) => {
let username = req.query.username || '';
const password = req.query.password || '';
username = username.replace(/[!@#$%^&*]/g, '');
if (!username || !password || !users[username]) {
return res.sendStatus(400);
}
const { salt, hash } = users[username];
const encryptHash = crypto.pbkdf2Sync(password, salt, 10000, 512, 'sha512');
if (crypto.timingSafeEqual(hash, encryptHash)) {
res.sendStatus(200);
} else {
res.sendStatus(401);
}
});
const password = "my_password";
// Creating a unique salt for a particular user
const salt = crypto.randomBytes(16).toString('hex');
// Hash the salt and password with 1000 iterations, 64 length and sha512 digest
const hash = crypto.pbkdf2Sync(password, salt, 1000, 64, 'sha512').toString('hex');
在数据库中存储用户的salt
和hash
。
const re_entered_password = "my_password";
// To verify the same - salt (stored in DB) with same other parameters used while creating hash (1000 iterations, 64 length and sha512 digest)
const newHash = crypto.pbkdf2Sync(re_entered_password, salt, 1000, 64, 'sha512').toString('hex');
// check if hash (stored in DB) and newly generated hash (newHash) are the same
hash === newHash;
crypto.timingSafeEqual
来进行比较。 - disfatedrandomBytes(16)
而不是8
吗?即使官方的 Node 文档也建议使用 16 字节(最小)的盐。 - machineghostscryptSync()
而不是等待scryptAsync()
了。 - undefined