Nodejs进阶:MD5简介及crypto模块的应用欢迎加入群交流,群号197339705计算机安全领域,主要用于保证消息的完整性和一致性。常见的应用场景包括密码保护、下载文件验证等。本文首先简单介绍一下MD5的特点和应用,然后着重介绍MD5在密码保护场景中的应用,最后通过实例简单介绍一下MD5碰撞。特点计算速度快:计算jquery.js的md5值,57254个字符,输出定长耗时1.907ms:输入长度不固定,输出长度固定(128位)。运算不可逆:当运算结果已知时,无法通过逆运算得到原始字符串。高度离散:输入的微小变化都会导致运算结果的巨大差异。弱碰撞:不同的输入可能具有相同的哈希值。应用场景文件完整性校验:比如从网上下载一个软件,一般网站都会在网页上附加该软件的md5值。比较md5值确保下载的软件完整(或正确)密码保护:将md5后的密码保存到数据库中,不保存明文密码,避免发生拖拽数据库等事件后明文密码泄露。防篡改:比如数字证书的防篡改就采用了摘要算法。(当然必须结合数字签名等手段)nodejs中md5操作示例在nodejs中,crypto模块封装了一系列密码学相关的函数,包括digest操作。基本的例子如下,很简单:varcrypto=require('crypto');varmd5=crypto.createHash('md5');varresult=md5.update('a').digest('hex');//输出:0cc175b9c0f1b6a831c399e269772661console.log(result);例子:密码保护前面说到,明文密码保存到数据库是非常不安全的,最坏的情况是md5之后再保存。例如用户密码为123456,运行md5后输出为:e10adc3949ba59abbe56e057f20f883e。这至少有两个好处:抗内部攻击:网站所有者不知道用户的明文密码,防止网站所有者利用用户的明文密码为非作歹。防外部攻击:如果网站被黑,黑客只能得到md5后的密码,不能得到用户的明文密码。示例代码如下:varcrypto=require('crypto');functioncryptPwd(password){varmd5=crypto.createHash('md5');returnmd5.update(password).digest('hex');}varpassword='123456';varcryptedPassword=cryptPwd(password);console.log(cryptedPassword);//Output:e10adc3949ba59abbe56e057f20f883e简单地对密码执行md5不是safe如前所述,对用户密码进行md5运算可以提高安全性。但实际上,这样的安全性很差,为什么呢?稍微修改一下上面的例子,说不定你就明白了。相同的明文密码具有相同的md5值。varcrypto=require('crypto');functioncryptPwd(password){varmd5=crypto.createHash('md5');returnmd5.update(password).digest('hex');}varpassword='123456';console.log(cryptPwd(password));//输出:e10adc3949ba59abbe56e057f20f883econsole.log(cryptPwd(password));//输出:e10adc3949ba59abbe56e057f20f883e当e10adc3949ba59abbe56e057f20f883e时,理论上可以猜到用户的明文密码是123456,其实彩虹表就是这样暴力破解的:预先计算存储普通明文密码的md5值,然后匹配网站数据库中保存的密码,可以快速查到用户的明文密码。(具体细节这里不探讨)那么,有什么办法可以进一步提高安全性呢?答案是:对密码加盐。给密码加盐“加盐”这个词看似神秘,其实原理很简单,就是在密码的特定位置插入特定的字符串后,再对修改后的字符串进行md5运算。例子如下。对于同一个密码,当“salt”值不同时,md5值的差异是非常大的。通过在密码中加盐,可以防止最基本的暴力破解。如果攻击者事先不知道“盐”值,破解起来会非常困难。varcrypto=require('crypto');functioncryptPwd(password,salt){//“salt”密码varsaltPassword=password+':'+salt;console.log('原密码:%s',password);console.log('加盐密码:%s',saltPassword);//加盐密码的md5值varmd5=crypto.createHash('md5');varresult=md5.update(saltPassword).digest('hex');console.log('加盐密码的md5值:%s',result);}cryptPwd('123456','abc');//输出://原始密码:123456//加盐后的密码:123456:abc//加盐后密码的md5值:51011af1892f59e74baf61f3d4389092cryptPwd('123456','bcd');//输出://原密码:123456//加盐后密码:123456:bcd//加盐后密码的md5值:55a95bcb6bfbaef6906dbbd264ab4531Passwordwithsalt:Randomsaltvalue在密码中加入salt可以大大提高密码的安全性。但实际上,上面的例子存在很多问题。假设字符串拼接算法和盐值已经泄露,上述代码至少存在以下问题:短盐值:穷举的可能性较小,容易暴力破解,一般使用长盐值来解决。固定salt值:同理,攻击者只需要计算常用密码的hash值表+salt值,就大功告成了。短盐值不用说,应该避免。为什么你不应该使用固定盐在这里需要更多的解释。在许多情况下,我们的盐值被硬编码到我们的代码中(例如配置文件)。一旦坏人通过某种方式知道了salt值,那么我们只需要对这串固定的salt值进行暴力耗尽即可。.比如上面的代码,当知道salt值是abc时,马上就能猜到51011af1892f59e74baf61f3d4389092对应的明文密码是123456。那么,如何优化呢?答案是:随机盐值。示例代码如下。可以看出密码也是123456,由于盐值随机,前后计算的结果不同。这样做带来的好处是多个用户,同一个密码,攻击者需要进行多次计算才能彻底破解。也是一个纯数字的3位短盐值。破解随机盐值所需的计算量是固定盐值的1000倍。varcrypto=require('crypto');functiongetRandomSalt(){returnMath.random().toString().slice(2,5);}functioncryptPwd(password,salt){//密码“salted”varsaltPassword=密码+':'+盐;console.log('原密码:%s',password);console.log('加盐密码:%s',saltPassword);//加盐密码的md5值varmd5=crypto.createHash('md5');varresult=md5.update(saltPassword).digest('hex');console.log('加盐后密码的md5值:%s',结果);}varpassword='123456';cryptPwd('123456',getRandomSalt());//输出://原始密码:123456//加盐后password:123456:498//加盐密码的md5值:af3b7d32cc2a254a6bf1ebdcfd700115cryptPwd('123456',getRandomSalt());//输出://原始密码:123456//加盐密码:123456:287//加盐密码的md5值:65d7dd044c2db64c5e658d947578d759MD5Collision简单的说就是两段是不同的String,经过MD5运算,得到相同的结果。网上例子很多,这里就不细说了,直接上例子,参考(这里)[http://www.mscs.dal.ca/~selin...functiongetHashResult(hexString){//转为十六进制,比如0x4d0xc9...hexString=hexString.replace(/(\w{2,2})/g,'0x$1').trim();//转换为十六进制数组,如[0x4d,0xc9,...]vararr=hexString.split('');//转换成对应的buffer,如:
