Nodejs 密码加密存储

参考链接:
https://www.zhihu.com/question/20820286
https://crackstation.net/hashing-security.htm
译文https://blog.csdn.net/coslay/article/details/50382252

密码在服务器一般不能明文存储,所以这里就涉及到加密处理的问题。
我们采用hash算法对密码进行加密后存储在数据库中,

下面简单介绍下hash算法

1 Hash

Hash,一般翻译做“散列”,也有直接音译为“哈希”的,就是把任意长度的输入(又叫做预哈希, pre-image),通过散列算法,变换成固定长度的输出,该输出就是散列值。简单的说就是一种将任意长度的消息压缩到某一固定长度的消息摘要的函数。具有单向的特点。

1.1 Hash的基本性质

  • 1 如果两个hash是不相同的(根据同一函数),那么这两个hash的原始输入也是不相同的。
  • 2 Hash函数的输入和输出不是一一对应的,如果两个散列值相同,两个输入值很可能是相同的。
  • 3 Hash函数是从一个非常大的取值空间映射到一个非常小的取值空间,由于不是一对一的映射,HASH函数转换后不可逆,即不可能通过逆操作和HASH值还原出原始的值,受到计算能力限制(注意,不是逻辑上不可能,前面的不可能是逻辑上的)而且也无法还原出所有可能的全部原始值。

这里我们总结出hash对于我们密码加密非常有用的两个特性:
1 不可逆,即hash过程过程不可逆,你不可以通过hash后的值得到原值。
2 无冲突,你知道一个值(x),但无法求出另一个值(y),使这两个值得hash值相同。

以上性质对于对于密码加密而言就是
(1)原始密码经哈希函数计算后得到一个哈希值
(2)改变原始密码,哈希函数计算出的哈希值也会相应改变
(3) 同样的密码,哈希值也是相同的
(4) 哈希函数是单向、不可逆的。也就是说从哈希值,你无法推算出原始的密码是多少

1.2 常用的hash实现

MD5:它对输入仍以512位分组,其输出是4个32位字的级联
SHA-1 SHA256等:它对长度小于264的输入,产生长度为160bit的散列值

1.3 hash算法加盐

密码中混入一段“随机”的字符串再进行哈希加密,这个被字符串被称作盐值

为什么要加盐:同一个密码经过哈希算法后得到的密码是一致,攻击者可以通过建立一个密码和哈希机密后的表的对应关系的表提高破解效率。

如果加盐,这使得同一个密码每次都被加密为完全不同的字符串。为了校验密码是否正确,我们需要储存盐值。通常和密码哈希值一起存放在账户数据库中,或者直接存为哈希字符串的一部分。

每次哈希加密都使用相同的盐值是很容易犯的一个错误,这个盐值要么被硬编码到程序里,要么只在第一次使用时随机获得。这样加盐的方式是做无用功,因为两个相同的密码依然会得到相同的哈希值。攻击者仍然可以使用反向查表法对每个值进行字典攻击,只需要把盐值应用到每个猜测的密码上再进行哈希即可。如果盐值被硬编码到某个流行的软件里,可以专门为这个软件制作查询表和彩虹表,那么破解它生成的哈希值就变得很简单了。
用户创建账户或每次修改密码时,都应该重新生成新的盐值进行加密。

1.4 慢哈希

加盐使攻击者无法采用特定的查询表和彩虹表快速破解大量哈希值,但是却不能阻止他们使用字典攻击或暴力攻击。高端的显卡(GPU)和定制的硬件可以每秒进行数十亿次哈希计算,因此这类攻击依然可以很高效。为了降低攻击者的效率,我们可以使用一种叫做密钥扩展的技术。

这种技术的思想就是把哈希函数变得很慢,于是即使有着超高性能的GPU或定制硬件,字典攻击和暴力攻击也会慢得让攻击者无法接受。最终的目标是把哈希函数的速度降到足以让攻击者望而却步,但造成的延迟又不至于引起用户的注意。这里推荐一种标准算法:bcrypt

这类算法使用一个安全因子或迭代次数作为参数,这个值决定了哈希函数会有多慢。对于桌面软件或者手机软件,获取参数最好的办法就是执行一个简短的性能基准测试,找到使哈希函数大约耗费0.5秒的值。这样,你的程序就可以尽可能保证安全,而又不影响到用户体验。

2 nodejs 密码哈希方案

2.1 实现原理

如何生成密文
1).首先我们得到的是明文的hash值
2).进行计算获取MD5明文hash值
3).随机生成加盐值并插入
4).MD5插入加盐值得到的hash
5).得到最终的密文

如何验证密码
得到用户的密码哈希值和对应盐值
将盐值混入用户输入的密码,并且使用同样的哈希函数进行加密
比较上一步的结果和数据库储存的哈希值是否相同,如果相同那么密码正确,反之密码错误

2.2 nodejs加密方案

bcryptjs是一个第三方密码加密库,自带加盐和慢哈希功能
模块地址:https://www.npmjs.com/package/bcrypt
https://blog.csdn.net/beijiyang999/article/details/78436876

API 说明
1 生成加盐后的hash值

bcrypt.hash(myPlaintextPassword, saltRounds, function(err, hash) {
  // Store hash in your password DB.
});
  • myPlaintextPassword:待处理字符串
  • saltRounds:慢哈希轮数

2 验证密文是否正确

  bcrypt.compare(password, encryptPassword, function(err, res) {
    if (res === true)
        console.log("password is true");
    })
  • Password: 未加密密码
  • encryptPassword:密文
  • res 为true 或者false

Sample code:

var bcrypt  = require('bcrypt');
var password = "helloworld";

bcrypt.hash(password, 10, function(err, encryptPassword) {
    console.log("bcrypt password:"+encryptPassword);
    bcrypt.compare(password, encryptPassword, function(err, res) {
    if (res === true)
        console.log("password is true");
    })
});

bcrypt.hash(password, 8, function(err, encryptPassword1) {
    console.log("bcrypt password:"+encryptPassword1);
    bcrypt.compare(password, encryptPassword1, function(err, res) {
    if (res === true)
        console.log("password is true");
    })
});

输出

bcrypt password:$2b$08$dmg3Epmn3KGOBbB/15yb/OR8PNiKVwt1c5FTuuIy6sHHlvubMi9oq
password is true
bcrypt password:$2b$10$CGa8jvxFcCX5IwsS8HAUiOIaYjkWR45eUpD/MMLxT/gR1T7ro1XP2
password is true

关于该模块盐位置说明,盐值本身就在 处理后的字符串中,我们用于验证时,模块会提取出盐值进行验证。
加密后的字符串还可以看的出crypt()版本 和加密轮数

猜你喜欢

转载自blog.csdn.net/m0_37263637/article/details/80411168