龙空技术网

一百年也不能破解的密码,打死也不怕黑客攻击!

滴滴一说 1701

前言:

目前小伙伴们对“sha512加密算法”都比较重视,兄弟们都需要分析一些“sha512加密算法”的相关知识。那么小编在网摘上汇集了一些关于“sha512加密算法””的相关资讯,希望大家能喜欢,看官们快快来了解一下吧!

经过数学证明,有一种加密密码,只要使用正确就不会被破解,但是这种密码现在使用的机会不多了。这就是作者给我们介绍的一次性密钥(one-time pad)。

从我们的经验来看,某种加密方式,不论它在刚出现时宣称有多么牢不可破,随着时间的迁移,它总会暴露出弱点,从一个强大的加密方式变成一个较弱的加密方式。而如今人们在使用目前流行的加密方式时,也有这样的忧虑,担心某一天会有一种捷径出现,使得目前所使用的看似无懈可击的加密手段在可控的时间内就被暴力破解。

就算目前所使用的那些还没有被破解的加密方式,在不断升级的CPU时钟频率前提下,暴力破解所需的时间也越来越短了。比如几个月前需要耗时一年才能破解的密码,以现在的计算速度来看,可能只需要七八个月就可以完成了。正因为计算速度越来越快,使得我们通过暴力手段破解密码的时间大大缩短了。

就算是以难以通过暴力方式破解为目的构建的更复杂更聪明的加密算法,在未来某天,也不可避免的会步那些已经被证明不够安全的加密算法的后尘。这就好像是冷战时期的军备竞赛,一方想竭力保护隐私,另一方却竭力窥探隐私。

在加密算法的发展过程中,有很多算法都已经被破解或被证明不够安全,唯有一种加密算法一直存在了93年。这种算法叫做一次性密钥(one-time pad)。在1917年, Gilbert Vernam开发了一种叫做Vernam Cipher 的加密方法,使用电传技术,通过打有密钥的纸带进行数据加密和解密。结果便出现了当时最强的对称加密技术。

美国陆军上尉Joseph Mauborgne注意到,这是真正随机产生的密钥,不会有任何重复的可能,因此Vernam cipher可以做到更强大的效果。于是,基于纸带密钥的方法,在一沓纸上印有随即字母或数字,作为密钥的加密算法出现了。相同的一沓纸可以给两个人用,每个页面上的每个字符只能用一次(每张纸上的字符或数字用完,则销毁该密码纸),这种方式可以避免敌人通过枚举法暴力破解信息。由于这种分法密钥流数据的技术是基于可销毁的纸张的,因此也被认为是一次性密钥。

信息理论之父Claude Shannon曾经通过数学方法,证明这种一次性密钥如果使用得当,是无法被破解的,而所谓的使用得当,是指及时销毁所使用的密钥页,即使对方拿到了密码本的其余部分,也无法进行解密。同样的理念也可以用于数字系统,但是要确保计算机具有足够的安全措施以及全面的考虑,防止黑客入侵一次性密钥系统。比如,一些昂贵的数据恢复系统会将已删除的文件恢复,其中就有可能恢复那些一次性的密钥文件。因此如果使用数字化的一次性密钥系统,就要确保所删除的密钥文件是被彻底删除,无法恢复的。

一次性密钥加密方式有时候非常不方便,因此现在很少有人在用这种加密方式。而正是由于这种不便性,我们实际需要的是一些理论上有些弱的加密方式,比如AES/Rijndael 以及Twofish 。一次密钥的不方便性在于:

由于一次性密钥是一种对称加密方式,进行加密通信的双方需要拥有完全一样的密钥数据。而在某些环境下,这种条件是无法实现的,因为要想让双方都拥有这个密钥,就意味着必须有一种足够安全的方式让双方共享或传递密钥数据,而如果有了这样的安全环境,也就不需要再使用一次性密钥了。而一般来说通过物理方式传递密钥(比如亲手交给对方)才能实现一次性密钥的优势。

一次性密钥必须和所加密内如一样长。这意味着,如果你要对一个3GB的文件进行加密,就需要有一个3GB的一次性密钥。

相同的一次性密钥只能在两人间保存,如果超过两人知晓这个密钥,就不再安全了。比如,如果在多个人之间传送不同的信息,并且不能让接受者获知其它人所接受的信息内容,只用一个相同的密钥是完全达不到加密效果的。相反,如果采用非对称的加密方式,我们就只需提供一个唯一的公钥给人们,每个人都通过自己的私钥对数据进行加密解密,他们彼此之间是无法获知对方所加密的信息内容的,除非私钥被盗取或者通过日益强大的计算机系统经过暴力破解出来。这是因为当信息被公钥加密后,只有相关的私钥才可以将其解密。

重复使用一次性密钥存在潜在的安全风险,因为它面临着已知明文漏洞。Kerckhoffs的理论是,保证一次性密钥系统安全的前提是保证密钥安全,但是在米加密信息和明文信息共存的时候,可以倒推出这个一次性密钥。当然,如果每一段密钥都仅使用一次,那么安全性仍然没有问题,因为即使“敌人”同时获取了秘文和明文,并破解出了密钥,那这段密钥也是已经被遗弃的密钥,对于其它信息的破译毫无用处。而如果密钥被重复利用,那么以获取的明文信息就可以成为破解新加密信息的一个重要工具。这就是为什么这种加密系统被称为“一次性”密钥系统。

当使用过一次性密钥后,不能再使用这个密钥对其它要发送的数据进行加密。如果通信的双方分别位于地球的两端,那么这种通信方式会让人抓狂。

另外还有其它一些因素也使得一次性密钥系统在某些环境下变得毫无实用性。但是这也给了我们一个理论知识,让我们能明白那些理论上较弱的密钥系统为什么还那么重要。

一次性密钥系统的工作方式很简单。它只是简单的对两组数据进行运算,比如两个字母或两个数字,并通过运算得出新的加密数据。而这个运算是针对待加密数据中的每一位数据的。运算过程很简单,一次一位数据。比如 XOR运算就可以用来进行这样的加密。我们举个最简单的例子,使用XOR运算对二进制数列进行加密:

首先,你需要一段信息。假设我们把“short”这个词作为信息,它确实不长。

接下来把这段信息转换成二进制形式。我们可以使用ASCII编码来转换 “short”这个词,转换后会得到下面这一串二进制信息:

3. 0111001101101000011011110111001001110100

接下来你需要一个和上面二进制串长度完全一致的密钥。在这个例子中,我们可以使用下面这串二进制字符作为密钥:

5. 0110010101101010001110010010011101100100

最后,我们将这两组字符串进行XOR 运算,基本上就是一个简单的减法。也就是说,信息中第一个字符是0,密钥的第一个字符也是0,那么0 - 0 = 0。同样,信息中第二个字符是1,密钥的第二个字符也是1, 1 - 1 = 0。接下来都是这样计算,计算中除了0外,还可能出现1 或 -1。此时对结果取绝对值,即不论1还是-1,均作为1。其运算过程和结果如下:

7. 0111001101101000011011110111001001110100

8. 0110010101101010001110010010011101100100

9. ----------------------------------------

10. 0001011000000010010101100101010100010000

当然,由于我们对结果的二进制串没有进行ASCII转换,因此很难看出它到底被加密成了什么样子。在电脑上进行ASCII编码很容易,手动编码则很麻烦,而对于数字编码,通过手动编码还是比较容易的。

一次性密钥系统的核心算法显然是相当简单的。只要围绕这个核心算法,设计加密软件的其余部分,并找到合适的密钥使用方法,那么这个新的加密软件就是真正安全的。换句话说,如果要找一个绝对安全有效不可能被破解的加密方式,那么只有使用一次性密钥系统了,前提是正确的使用它,即密钥只使用一次。

被脱裤也不怕,密码安全可以这样保障!

在前面,主要提出了一些设计的方法和思路,并没有给出一个更加具体的,可以实施的安全加密方案。经过我仔细的思考并了解了目前一些方案后,我设计了一个自认为还比较安全的安全加密方案。本文主要就是讲述这个方案,非常欢迎和期待有读者一起来讨论。

首先,我们明确一下安全加密方案的终极目标:

即使在数据被拖库,代码被泄露,请求被劫持的情况下,也能保障用户的密码不被泄露。

说具体一些,我们理想中的绝对安全的系统大概是这样的:

首先保障数据很难被拖库。 即使数据被拖库,攻击者也无法从中破解出用户的密码。 即使数据被拖库,攻击者也无法伪造登录请求通过验证。 即使数据被拖库,攻击者劫持了用户的请求数据,也无法破解出用户的密码。

如何保障数据不被拖库,这里就不展开讲了。首先我们来说说密码加密。现在应该很少系统会直接保存用户的密码了吧,至少也是会计算密码的 md5 后保存。md5 这种不可逆的加密方法理论上已经很安全了,但是随着彩虹表的出现,使得大量长度不够的密码可以直接从彩虹表里反推出来。

所以,只对密码进行 md5 加密是肯定不够的。聪明的程序员想出了个办法,即使用户的密码很短,只要我在他的短密码后面加上一段很长的字符,再计算 md5 ,那反推出原始密码就变得非常困难了。加上的这段长字符,我们称为盐(Salt),通过这种方式加密的结果,我们称为 加盐 Hash 。比如:

salt

上一篇我们讲过,常用的哈希函数中,SHA-256、SHA-512 会比 md5 更安全,更难破解,出于更高安全性的考虑,我的这个方案中,会使用 SHA-512 代替 md5 。

salt

通过上面的加盐哈希运算,即使攻击者拿到了最终结果,也很难反推出原始的密码。不能反推,但可以正着推,假设攻击者将 salt 值也拿到了,那么他可以枚举遍历所有 6 位数的简单密码,加盐哈希,计算出一个结果对照表,从而破解出简单的密码。这就是通常所说的暴力破解。

为了应对暴力破解,我使用了加盐的慢哈希。慢哈希是指执行这个哈希函数非常慢,这样暴力破解需要枚举遍历所有可能结果时,就需要花上非常非常长的时间。比如:bcrypt 就是这样一个慢哈希函数:

bcrypt

通过调整 cost 参数,可以调整该函数慢到什么程度。假设让 bcrypt 计算一次需要 0.5 秒,遍历 6 位的简单密码,需要的时间为:((26 * 2 + 10)^6) / 2 秒,约 900 年。

好了,有了上面的基础,来看看我的最终解决方案:

password_secutity

上图里有很多细节,我分阶段来讲:

1. 协商密钥

基于非对称加密的密钥协商算法,可以在通信内容完全被公开的情况下,双方协商出一个只有双方才知道的密钥,然后使用该密钥进行对称加密传输数据。比如图中所用的 ECDH 密钥协商。

2. 请求 Salt

双方协商出一个密钥 SharedKey 之后,就可以使用 SharedKey 作为 AES 对称加密的密钥进行通信,客户端传给服务端自己的公钥 A ,以及加密了的用户ID(uid)。服务端从数据库中查找到该 uid 对于的 Salt1 和 Salt2 ,然后再加密返回给客户端。

注意,服务端保存的 Salt1 和 Salt2 最好和用户数据分开存储,存到其他服务器的数据库里,这样即使被 SQL 注入,想要获得 Salt1 和 Salt2 也会非常困难。

3. 验证密码

这是最重要的一步了。客户端拿到 Salt1 和 Salt2 之后,可以计算出两个加盐哈希:

SaltHash1 = bcrypt(SHA512(password), uid + salt1, 10)SaltHash2 = SHA512(SaltHash1 + uid + salt2)

使用 SaltHash2 做为 AES 密钥,加密包括 uid,time,SaltHash1,RandKey 等内容传输给服务端:

Ticket = AES(SaltHash2, uid + time + SaltHash1 + RandKey)AES(SharedKey, Ticket)

服务端使用 SharedKey 解密出 Ticket 之后,再从数据库中找到该 uid 对应的 SaltHash2 ,解密 Ticket ,得到 SaltHash1 ,使用 SaltHash1 重新计算 SaltHash2 看是否和数据库中的 SaltHash2 一致,从而验证密码是否正确。

校验两个哈希值是否相等时,使用时间恒定的比较函数,防止试探性攻击。

time 用于记录数据包发送的时间,用来防止录制回放攻击。

4. 加密传输

密码验证通过后,服务端生成一个随机的临时密钥 TempKey(使用安全的随机函数),并使用 RandKey 做为密钥,传输给客户端。之后双方的数据交互都通过 TempKey 作为 AES 密钥进行加密。

假设被拖库了

以上就是整个加密传输、存储的全过程。我们来假设几种攻击场景:

假设数据被拖库了,密码会泄露吗?

数据库中的 Salt1 ,Salt2 , SaltHash2 暴露了,想从 SaltHash2 直接反解出原始密码几乎是不可能的事情。

假设数据被拖库了,攻击者能不能伪造登录请求通过验证?

攻击者在生成 Ticket 时,需要 SaltHash1 ,但由于并不知道密码,所以无法计算出 SaltHash1 ,又无法从 SaltHash2 反推 SaltHash1 ,所以无法伪造登录请求通过验证。

假设数据被拖库了,攻击者使用中间人攻击,劫持了用户的请求,密码会被泄露吗?

中间人拥有真实服务器所有的数据,仿冒了真实的 Server ,因此,他可以解密出 Ticket 中的 SaltHash1 ,但是 SaltHash1 是无法解密出原始密码的。所以,密码也不会被泄露。

但是,中间人攻击可以获取到最后的 TempKey ,从而能监听后续的所有通信过程。这是很难解决的问题,因为在服务端所有东西都暴露的情况下,中间人假设可以劫持用户数据,仿冒真实 Server , 是很难和真实的 Server 区分开的。解决的方法也许只有防止被中间人攻击,保证 Server 的公钥在客户端不被篡改。

假设攻击已经进展到了这样的程度,还有办法补救吗?有。由于攻击者只能监听用户的登录过程,并不知道真实的密码。所以,只需要在服务端对 Salt2 进行升级,即可生成新的 SaltHash2 ,从而让攻击者所有攻击失效。

具体是这样的:用户正常的登录,服务端验证通过后,生成新的 Salt2 ,然后根据传过来的 SaltHash1 重新计算了 SaltHash2 存入数据库。下次用户再次登录时,获取到的是新的 Salt2 ,密码没有变,同样能登录,攻击者之前拖库的那份数据也失效了。

Q & A

使用 bcrypt 慢哈希函数,服务端应对大量的用户登录请求,性能承受的了吗?

该方案中,细心一点会注意到, bcrypt 只是在客户端进行运算的,服务端是直接拿到客户端运算好的结果( SaltHash1 )后 SHA-512 计算结果进行验证的。所以,把性能压力分摊到了各个客户端。

为什么要使用两个 Salt 值?

使用两个 Salt 值,是为了防止拖库后,劫持了用户请求后将密码破解出来。只有拥有密码的用户,才能用第一个 Salt 值计算出 SaltHash1 ,并且不能反推回原始密码。第二个 Salt 值可以加大被拖库后直接解密出 SaltHash1 的难度。

为什么要动态请求 Salt1 和 Salt2 ?

Salt 值直接写在客户端肯定不好,而且写死了要修改还得升级客户端。动态请求 Salt 值,还可以实现不升级客户端的情况下,对密码进行动态升级:服务端可定期更换 Salt2 ,重新计算 SaltHash2 ,让攻击者即使拖了一次数据也很快处于失效状态。

数据库都已经全被拖走了,密码不泄露还有什么意义呢?

其实是有意义的,正如刚刚提到的升级 Salt2 的补救方案,用户可以在完全不知情的情况下,不需要修改密码就升级了账号体系。同时,保护好用户的密码,不被攻击者拿去撞别家网站的库,也是一份责任。

标签: #sha512加密算法