Nodejs下的Crypto模块提供了OpenSSL中的一系列hash、hmac、cipher、decipher、签名和验证等方法的封装。

Hash算法

哈希算法,是指将任意长度的二进制值映射为较短的固定长度的二进制值,这个小的二进制值称为哈希值。哈希值是一段数据唯一且极其紧凑的数值表示形式。如果散列一段明文而且哪怕只更改该段落的一个字母,随后的哈希都将产生不同的值。要找到散列为同一个值的两个不同的输入,在计算上是不可能的,所以数据的哈希值可以检验数据的完整性。一般用于快速查找和加密算法。

如果两个散列值是不相同的(根据同一函数),那么这两个散列值的原始输入也是不相同的。

如果两个散列值相同,两个输入值很可能是相同的。但也可能不同,这种情况称为「碰撞」,这通常是两个不同长度的散列值,刻意计算出相同的输出值。

通常登陆密码,都是使用Hash算法进行加密,典型的哈希算法包括’md5′,’sha’,’sha1′,’sha256′,’sha512′,’RSA-SHA’。

var crypto = require('crypto');

function md5(str) {
  var hash = crypto.createHash("md5");
  return hash.update(str).digest('hex');
}

function sha1(str) {
  var hash = crypto.createHash("sha1");
  return hash.update(str).digest('hex');
}

function sha256(str) {
  var hash = crypto.createHash('sha256');
  return hash.update(str).digest('hex');
}

一般的网站常用md5和sha1给用户密码加密存储。

Hmac算法

HMAC是密钥相关的哈希运算消息认证码(Hash-based Message Authentication Code),HMAC运算利用哈希算法,以一个密钥和一个消息为输入,生成一个消息摘要作为输出。HMAC可以有效防止一些类似md5的彩虹表等攻击,比如一些常见的密码直接MD5存入数据库的,可能被反向破解。

定义HMAC需要一个加密用散列函数(表示为H,可以是MD5或者SHA-1)和一个密钥K。我们用B来表示数据块的字节数。(以上所提到的散列函数的分割数据块字长B=64),用L来表示散列函数的输出数据字节数(MD5中L=16,SHA-1中L=20)。鉴别密钥的长度可以是小于等于数据块字长的任何正整数值。应用程序中使用的密钥长度若是比B大,则首先用使用散列函数H作用于它,然后用H输出的L长度字符串作为在HMAC中实际使用的密钥。一般情况下,推荐的最小密钥K长度是L个字节。

var crypto = require('crypto');

function hmacMd5(str, secret) {
  var hash = crypto.createHmac('md5', secret);
  return hash.update(str).digest('hex');
}

function hmacSha1(str, secret) {
  var hash = crypto.createHmac('sha1', secret);
  return hash.update(str).digest('hex');
}

function hmacSha256(str, secret) {
  var hash = crypto.createHmac('sha256', secret);
  return hash.update(str).digest('hex');
}

Salt算法

盐(Salt),在密码学中,是指通过在密码任意固定位置插入特定的字符串,让散列后的结果和使用原始密码的散列结果不相符,这种过程称之为“加盐”。加盐后的散列值,可以极大的降低由于用户数据被盗而带来的密码泄漏风险,即使通过彩虹表寻找到了散列后的数值所对应的原始内容,但是由于经过了加盐,插入的字符串扰乱了真正的密码,使得获得真实密码的概率大大降低。

加盐的实现过程通常是在需要散列的字段的特定位置增加特定的字符,打乱原始的字串,使其生成的散列结果产生变化。

我们可以通过Nodejs的强伪随机函数生成salt。

var crypto = require('crypto');

var passowrd = '123456';
var salt = crypto.randomFillSync(Buffer.alloc(20)).toString('hex');

// 加盐
var hash = sha1(password + salt);

Nodejs提供了一个pbkdf2()自动加盐函数,默认会调用hmac算法,用sha1的散列函数,并且可以设置迭代次数和密文长度。

// crypto.pbkdf2Sync(password, salt, iterations, keylen, digest)

var crypto = require('crypto');

var hash = crypto.pbkdf2Sync(password, salt, 3, 20, 'sha1');

案例

设计一个用户表user: id,username,password,salt,…
前端提交用户数据:username=test&password=123456

var crypto = require('crypto');
// 1、随机生成一个40位长度的salt
var salt = crypto.randomFillSync(Buffer.alloc(20)).toString('hex');

// 2、加salt后加密原始密码
var hash = sha1(password + salt);

// 或者通过Nodejs提供的pbkdf2函数加盐加密
var hash2 = crypto.pbkdf2Sync(password, salt, 3, 20, 'sha1');

// 将 hash, salt同时存储到用户表user:password,salt字段中

// 登录校验时,只需每次登录时按照上方的加盐方式加密,然后跟数据表中的password字段比对是否相同。

function sha1(str) {
  var hash = crypto.createHash("sha1");
  return hash.update(str).digest('hex');
}

参考

https://nodejs.org/api/crypto.html
Node.js加密算法库Crypto