本文转载自微信公众号《全栈修仙之路》,作者阿宝哥。转载本文请联系全栈修真之路公众号。在上传大文件的场景下,为了提升大文件上传的用户体验,我们将支持断点续传。在上传过程中,我们会对大文件进行分片,然后使用md5算法计算分片的哈希值,然后将分片的内容及其对应的哈希值提交给服务器。服务器收到分片对应的哈希值后,会先检查该哈希值是否已经存在。如果存在,则表示该片段已经上传。此时可以返回大文件上传的字节数,以便客户端继续上传剩余内容。事实上,分片对应的哈希值也可以称为分片的“数字指纹”,那么什么是“数字指纹”呢?要理解“数字指纹”,首先要理解什么是消息摘要算法。1、什么是消息摘要算法?消息摘要算法是密码算法中一个非常重要的分支。它从所有数据中提取指纹信息,实现数据签名、数据完整性验证等功能。由于其不可逆性,有时用于敏感信息的加密。消息摘要算法也称为散列(Hash)算法或散列算法。任何消息经过哈希函数处理后,都会得到一个唯一的哈希值。这个过程叫做“消息摘要”,它的哈希值叫做“数字指纹”,它的算法自然就是“消息摘要算法”。换句话说,如果他们的数字指纹匹配,则他们的消息是一致的。(图片来源-https://zh.wikipedia.org/wiki/Hashfunction)消息摘要算法不存在密钥管理和分发的问题,适合在分布式网络上使用。消息摘要算法主要用于“数字签名”领域,作为明文的摘要算法。众所周知的摘要算法包括RSA的MD5算法和SHA-1算法以及大量的变体。1.1消息摘要算法的特点无论输入的消息有多长,计算出的消息摘要的长度总是固定的。例如,经过MD5算法消化后的报文有128位,经过SHA-1算法消化后的报文最终输出为160位。SHA-1的变体可以生成192位和256位的消息摘要。.一般认为摘要的最终输出越长,摘要算法越安全。消息摘要似乎是“随机的”。这些位似乎是随机混合在一起的,大量的输入可以用来检查输出是否相同。一般来说,不同的输入会有不同的输出,输出的摘要消息可以通过随机性测试。一般来说,只要输入消息不同,消化后生成的摘要消息也必然不同;但相同的输入必须产生相同的输出。消息摘要功能是一种单向功能,即只能进行前向信息摘要,而不能从摘要中恢复出任何消息,甚至根本找不到与原始信息相关的任何信息。一个好的摘要算法,没有人能找到“碰撞”或极难找到,尽管“碰撞”肯定存在(碰撞是不同的内容产生相同的摘要)。1.2消息摘要算法谱系消息摘要算法主要分为三类:MD(MessageDigest,消息摘要算法)、SHA-1(SecureHashAlgorithm,安全散列算法)和MAC(MessageAuthenticationCode,消息认证码算法)。MD系列算法包括3种算法:MD2、MD4、MD5;SHA算法主要包括其代表算法SHA-1和变种SHA-1算法SHA-2系列算法(包括SHA-224、SHA-256、SHA-384和SHA-512);MAC算法综合了以上两种算法,主要包括HmacMD5、HmacSHA1、HmacSHA256、HmacSHA384和HmacSHA512算法。以上内容虽然列举了各种消息摘要算法,但仍然不能满足应用需求。基于这些消息摘要算法,衍生出RipeMD系列(包括RipeMD128、RipeMD160、RipeMD256、RipeMD320)、Tiger、GOST3411和Whirlpool算法。对于大部分前端开发者来说,MD5算法应该接触的比较多。那么,接下来,阿宝哥就把重点放在MD5算法上。2、什么是MD5算法?MD5(MessageDigestAlgorithm5,MessageDigestAlgorithmVersion5)是由MD2、MD3、MD4发展而来的,由RonRivest(RSA公司)于1992年提出,目前广泛应用于数据完整性。性别验证、数据(消息)摘要、数据签名等。MD2、MD4、MD5都会产生一个16字节(128位)的校验值,一般用32位的十六进制数表示。MD2是一种速度较慢但相对安全的算法,MD4速度较快但安全性较差,而MD5比MD4更安全且速度更快。随着计算机技术的发展和计算水平的不断提高,MD5算法暴露出的漏洞越来越多。1996年后确认存在弱点,可以破解。对于安全性要求高的数据,专家一般建议使用其他算法,如SHA-2。2004年确认MD5算法无法防止碰撞(collision),因此不适用于安全认证,如SSL公钥认证或数字签名。2.1MD5特点运行稳定、快速。可压缩性:输入任意长度的数据,输出长度固定(128位)。运算不可逆:当运算结果已知时,无法通过逆运算得到原始字符串。高度离散:输入的微小变化都会导致运算结果的巨大差异。2.2MD5哈希一个128位的MD5哈希在大多数情况下会被表示为一个32位的十六进制数。以下是一个43位长的ASCII-only列的MD5散列:MD5("Thequickbrownfoxjumpsoverthelazydog")=9e107d9d372bb6826bd81d3542a419d6即使对原始文本进行了微小的更改(例如将dog更改为cog,仅更改一个字符),散??列也会发生巨大的变化:MD5("Thequickbrownfoxjumpsoverthelazycog")=1055d3e698d289f2af8663725127bd4b接着我们再来举几个MD5散列的例子:MD5("")->d41d8cd98f00b204e9800998ecf8427eMD5("semlinker")->688881f1c8aa6ffd3fcec471e0391e4dMD5("kakuqo")->e18c3c4dd05aef020946e6afbf9e04ef三3.MD5算法的用途3.1防篡改3.1.1文件分发防篡改在Internet上分发软件安装包时,出于安全考虑,为了防止软件被篡改,例如在软件安装程序中加入木马程序。软件开发人员通常使用消息摘要算法,如MD5算法,生成与文件匹配的数字指纹,以便接收方在收到文件后,可以使用一些现成的工具来检查文件的完整性.(图片来源——https://en.wikipedia.org/wiki/MD5)这里我们举一个实际的例子。下图是MySQLCommunityServer8.0.19版本的下载页面。下载页面通过MD5算法分别计算出不同软件包的数字指纹,如下图:(图片来源-https://dev.mysql.com/downloads/mysql/)用户下载后从官网下载相应的安装包,他可以使用一些MD5校验工具对下载的文件进行校验,然后比对最终的MD5数字指纹。如果结果与官网公布的数字指纹一致,说明安装包没有任何修改是安全的,基本可以放心安装。3.1.2消息传输的防篡改假设您需要向网络上的朋友发送电子文档。“数字指纹”随电子文件一起发送给对方。对方在收到电子文档时,同样使用MD5算法对文档内容进行哈希运算。运算完成后,也会得到对应的“数字指纹”。当指纹与您发送的文件的“数字指纹”一致时,表明该文件在传输过程中未被篡改。3.2信息保密性在互联网发展初期,很多网站将用户密码以明文形式存储在数据库中,存在很大的安全隐患,如数据库被黑客攻击,导致网站用户信息泄露。为了解决这个问题,一种解决方案是在保存用户密码时使用消息摘要算法代替明文,例如MD5算法对明文密码进行哈希运算,然后将运算结果保存到数据库中。使用上述方案避免了将密码明文保存在数据库中,提高了系统的安全性。不过这个方案并不安全,后面我们会详细分析。当用户登录时,登录系统对用户输入的密码进行MD5哈希运算,然后使用用户ID和密码对应的MD5“数字指纹”进行用户认证。如果认证通过,则当前用户可以正常登录系统。用户密码经过MD5哈希运算后存储的方案至少有两个优点:抗内部攻击:由于密码在数据库中不是明文存储的,可以防止用户在系统中的密码被系统管理员等人员破解特权;知道。防外部攻击:网站数据库被黑,黑客只能通过MD5计算得到密码,不能得到用户的明文密码。4、MD5算法使用示例在Node.js环境下,我们可以使用crypto原生模块提供的md5实现,当然也可以使用主流的MD5第三方库,比如md5,可以运行在服务器和客户端上。.在介绍具体使用之前,我们需要提前安装第三方库md5。具体安装方法如下:$npminstallmd5--save4.1Crypto模块使用示例returnhash.update(data).digest("hex");}console.log("Node.jsCryptoMD5:"+msg+"->"+md5(msg));4.2MD5库使用示例constmd5=require('md5');constmsg="Abaoge";console.log("MD5LibMD5:"+msg+"->"+md5(msg));以上示例代码正常运行后,会在控制台输出如下结果:Node.jsCryptoMD5:阿宝哥->8eec7fcf817f7340b791b32ecdbed570MD5LibMD5:阿宝哥->8eec7fcf817f7340b791b32ecdbed5705.MD5算法的缺陷在于hash碰撞意味着不同的输入产生同样的输出,OK没有人能从中找到“碰撞”或者极难从中找到“碰撞”,尽管“碰撞”肯定存在。2005年,山东大学王小云教授发布了一种可以轻松构建MD5碰撞实例的算法。2007年,国外有学者在王晓云教授算法的基础上,进一步提出了MD5前缀碰撞构建算法“chosenprefixcollision”。此后,陆续有专家提供了MD5碰撞构建的开源库。2009年,中科院的谢涛和冯登国仅用了220.96的碰撞算法复杂度就破解了MD5的抗碰撞性。这种攻击在普通计算机上运行只需几秒钟。MD5碰撞构造简单,基于MD5验证数据完整性不可靠。考虑到谷歌最近成功构建了SHA-1(英文:SecureHashAlgorithm1,中文名称:SecureHashAlgorithm1)的碰撞实例,为了数据完整性,应该改用SHA256或者更强的算法。5.1MD5碰撞样本下面我们先来看一下MD5碰撞的样本:5.1.1HEX(十六进制)样本A14dc968ff0ee35c209572d4777b721587d36fa7b21bdc56b74a3dc0783e7b9518afbfa200a8284bf36e8e4b55b35f427593d849676da0d1555d8360fb5f07fea25.1.2HEX(十六进制)样本A24dc968ff0ee35c209572d4777b721587d36fa7b21bdc56b74a3dc0783e7b9518afbfa202a8284bf36e8e4b55b35f427593d849676da0d1d55d8360fb5f07fea2两个样本之间的差异如下图所示:5.2验证MD5碰撞下面我们来使用Node.js实际验证一下样本A1和样本A2经过MD5运算后输出的结果是否一致:5.2.1设置样本数据letsample1=`4dc968ff0ee35c209572d4777b721587d36fa7b21bdc56b74a3dc0783e7b9518afbfa200a8284bf36e8e4b55b35f427593d849676da0d1555d8360fb5f07fea2`;letsample2=`4dc968ff0ee35c209572d4777b721587d36fa7b21bdc56b74a3dc0783e7b9518afbfa202a8284bf36e8e4b55b35f427593d849676da0d1d55d8360fb5f07fea2`;5.2.2定义getHashResult方法functiongetHashResult(hexString){consthash=crypto.createHash("md5");constbuffer=Buffer.from(hexString.replace(/\s/g,""),"hex");returnhash.update(buffer).digest("hex");}5.2.3Performcollisiondetectionletsample1Md5=getHashResult(sample1);letsample2Md5=getHashResult(sample2);if(sample1Md5===sample2Md5){console.log(`MD5碰撞:${sample1Md5}`);}else{console.log(`没有MD5碰撞`);}上述代码运行成功后,控制台会输出如下结果:MD5collision:008ee33a9d58b51cfeb425b0959121c9如果你对其他MD5碰撞样本感兴趣,可以查看一些MD5碰撞例子。本文基于MD5来验证数据完整性是不可靠的,因此Node.js使用SHA256算法来确保数据完整性。(图片来源——https://nodejs.org/download/release/v15.6.0/SHASUMS256.txt.asc)6.MD5密码安全6.1MD5密文反向查询MD5运算可以提高系统的安全性。但实际上,这样的安全性仍然不高。为什么?因为只要输入相同,它就会产生相同的输出。接下来,我们举个例子。字符串123456789是一个非常常用的密码。经过MD5运算后会生成对应的hash值:MD5("123456789")->25f9e794323b453885f5181f1b624d0b由于相同的输入会产生相同的结果,所以攻击者可以根据hash结果对输入进行逆向。一种常见的破解方法是使用彩虹表。彩虹表是用于加密哈希函数逆运算的预计算表,通常用于破解加密的密码哈希。查找表通常用于加密包含有限字符的固定长度的明文密码。这是一种典型的以空间换取时间的做法,在每次尝试计算的暴力攻击中使用更少的计算能力和更多的存储空间,但比使用每个输入具有哈希的简单查找表使用更多的空间。更少的存储空间和更高的计算性能。目前网上一些站点,如cmd5.com,已经为我们提供了MD5密文的反向查询服务。我们用MD5生成的结果(“123456789”)做一个简单的验证,如下图所示:因为123456789是一个很常见的密码,所以网站能够反推正确的结果也就不足为奇了.以下是cmd5网站的站点说明,大家可以参考一下,有兴趣的朋友可以亲自去验证一下。现在我们知道,如果用户的密码相同,则MD5值也会相同。通过一些MD5密文的反向查询网站,密码会被大概率解析出来,所以使用相同密码的用户会受到影响。那么如何解决这个问题呢?答案是在密码中加盐。6.2密码盐(Salt),在密码学中,是指在散列前将散列内容(例如:密码)的任意固定位置插入到特定的字符串中。这种将字符串添加到散列的方法称为“加盐”。它的作用是使加盐后的哈希结果与不加盐的结果不同。在不同的应用场景中,这种处理可以增加额外的安全性。在大多数情况下,盐不需要保密。salt可以是随机生成的字符串,其插入位置可以是任意的。如果以后需要验证这个散列结果(例如:验证用户输入的密码),需要记录使用的盐。为了便于理解,我们举个简单的例子。6.2.1Node.jsMD5加盐示例console.log("加盐密码:%s",saltPassword);constmd5=crypto.createHash("md5");constresult=md5.update(saltPassword).digest("hex");console.log("md5值加盐密码:%s",result);}cryptPwd("123456789","exe");cryptPwd("123456789","eft");以上示例代码正常运行后,控制台会输出如下结果:原密码:123456789saltedpassword:123456789:exesaltedpassword的md5值:3328003d9f786897e0749f349af490ca原密码:123456789saltedpassword:123456789:eft221bad6d5d5saltedpassword的md5值:发现原来的密码是一致的,只是使用的salt值不同,得到的MD5哈希值也相差较大。另外,为了增加破解难度,我们可以随机生成一个盐值,并增加盐值的长度。6.3bcrypt散列加盐的方法确实可以增加攻击者的成本,但放在今天还远远不够。我们需要一种更安全的方式来存储用户密码,这就是今天广泛使用的bcrypt。bcrypt是由NielsProvos和DavidMazières基于Blowfish加密算法设计的密码哈希函数,并于1999年在USENIX上提出。bcrypt算法是专门为密码哈希而设计的,所以是一个比较慢的算法,可以减少攻击者每秒可以处理的密码数量,从而避免攻击者的字典攻击。在实现中,bcrypt将使用加盐过程来防御彩虹表攻击。同时,bcrypt也是一个自适应函数,可以通过增加迭代次数来抵抗计算机算力不断增加的暴力破解。通过bcrypt加密的文件可以在所有支持的操作系统和处理器上传输。其密码必须为8到56个字符,并将在内部转换为448位密钥。但是,提供的所有字符都具有重要意义。您的密码越强,您的数据就越安全。下面以Node.js平台的bcryptjs为例,介绍如何使用bcrypt算法处理用户密码。首先我们需要安装bcryptjs:$npminstallbcryptjs--save6.3.1使用bcryptjs处理密码constbcrypt=require("bcryptjs");constpassword="123456789";constsaltRounds=10;asyncfunctionbcryptHash(str,saltRounds){lethashedResult;try{constsalt=awaitbcrypt.genSalt(saltRounds);hashedResult=awaitbcrypt.hash(str,salt);}catch(error){throwerror;}returnhashedResult;}bcryptHash(password,saltRounds).then(console.log);在上面的示例代码之后正常运行,控制台会输出如下结果:$2a$10$O1SrEy3KsgN0NQdQjaSU6OxjxDo0jf.j/e2goSwSEu4esz9i58dRm显然,密码123456789经过bcrypt哈希后,得到了一串不可读的“乱码”。到这里我们就完成了第一步,即用户登录密码的加密。下一步,我们需要实现登录密码的比对,即保证用户在输入正确的密码后能够正常登录系统。6.3.2使用bcryptjs验证密码asyncfunctionbcryptCompare(str,hashed){letisMatch;try{isMatch=awaitbcrypt.compare(str,hashed);}catch(error){throwerror;}returnisMatch;}bcryptCompare("123456789","$2A$10$O1SREY3KSGN0NQJASU6OXJXDO0JF.J/e2goswseu4esz9i58drm“),控制台会输出如下结果:truefalse因为我们原来的密码是123456789,显然不匹配123456,所以会输出上面的匹配结果。7.总结本文首先介绍了消息摘要算法和MD5算法的相关概念和特点,然后详细介绍了MD5算法的使用和Node.js平台的使用实例。最后,我们还分析了MD5算法和MD5密码的缺陷。安全问题。这里需要大家注意的是,基于MD5来校验数据的完整性是不可靠的。考虑到谷歌最近成功构建了SHA-1(英文:SecureHashAlgorithm1,中文名称:SecureHashAlgorithm1)的碰撞实例,对于数据完整性检查,应该改用SHA256或更强的算法。除了本文介绍的MD5应用场景,MD5还可以用来实现CDN(ContentDeliveryNetwork,内容分发网络)内容资源的防盗链。感兴趣的小伙伴可以阅读《深入理解Token反盗链》一文。最近阿宝哥突然对JS逆向很感兴趣,所以打算系统的学习一下相关知识。目前,他已经有了初步的学习计划。想一起学习的小伙伴可以私聊阿宝格哈。8.参考资源维基百科-MD5维基百科-彩虹表维基百科-Salt_(Cryptography)加密基础1MD5SHA-1CRCSaltMD,SHA,MACMessageDigestAlgorithmImplementationandApplicationMD5冲突的一些例子
