问题

先用md5,再用sha1,这样密码会安全一点吗?

回答
将密码先用MD5加密,然后再用SHA1加密,不会让密码变得更安全,反而可能带来一些意想不到的负面影响。

让我们详细地分析一下原因:

1. 什么是密码哈希?

在讨论加密之前,我们需要理解“哈希”的概念。密码哈希是一个单向的数学函数,它将任意长度的输入(你的密码)转换成固定长度的输出(哈希值)。这个过程的特点是:

单向性 (Oneway): 从哈希值很难(理想情况下不可能)反推出原始密码。
确定性 (Deterministic): 相同的输入总是产生相同的输出。
雪崩效应 (Avalanche Effect): 输入的微小改变会导致输出的巨大变化。

为什么我们用哈希而不是直接存储密码?

直接存储用户的明文密码是极不安全的。一旦数据库泄露,所有用户的密码都会暴露。使用哈希值可以保护用户在数据库泄露时的安全,因为攻击者即使获得了哈希值,也无法直接知道用户的密码。

2. 为什么“MD5后SHA1”不安全,甚至适得其反?

这里的“加密”实际上是指“哈希”。当你先用MD5哈希一个密码,然后再用SHA1哈希MD5的输出结果,这个过程可以被看作是一个组合哈希。理论上,组合哈希的目的是增加计算的复杂性,从而让攻击者更难破解。然而,对于MD5和SHA1来说,这种组合并没有带来预期的安全提升,反而存在以下几个问题:

MD5本身已经过时且不安全 (Vulnerable to Collisions):
MD5的碰撞攻击 (Collision Attack) 已经非常成熟。这意味着攻击者可以找到两个不同的输入,产生相同的MD5哈希值。
举个例子:假设用户密码是“password123”。MD5("password123") 得到一个哈希值 `H1`。如果攻击者找到了另一个完全不同的字符串 `X`,使得 MD5(`X`) 也等于 `H1`。那么,你存储的 `SHA1(H1)` 将与 `SHA1(MD5("X"))` 相等。攻击者只需要找到一个能生成相同MD5哈希值的替代输入 `X`,就可以绕过你的安全措施。
这意味着MD5的第一个哈希层已经被攻破了,后面的SHA1保护作用非常有限。

SHA1也存在安全漏洞 (Weaknesses in SHA1):
虽然SHA1比MD5稍微强一些,但它也已经不再被认为是安全的加密哈希算法。SHA1也存在碰撞漏洞,尽管找到碰撞比MD5更难,但也不是不可能。
谷歌在2017年已经宣布成功制造出SHA1碰撞,这表明SHA1的安全性已经受到严重威胁。

增加计算成本,但未能增加实质安全:
虽然理论上计算两次哈希会比计算一次更慢,但这并不能阻止现代的暴力破解或彩虹表攻击。这些攻击方法主要是针对哈希算法的固有弱点,而不是简单的计算量。
真正能有效抵御暴力破解的是加盐 (Salting) 和密钥派生函数 (Key Derivation Functions KDFs),而不是简单地嵌套哈希算法。

误导性的安全感 (False Sense of Security):
许多人会认为,使用多种哈希算法叠加会更安全,但实际上是基于对哈希算法安全性理解的误区。这种做法并没有解决核心问题。

3. 为什么更好的密码安全方法是加盐和使用密钥派生函数?

现代密码安全实践强调使用加盐 (Salting) 和强大的密钥派生函数 (Key Derivation Functions KDFs),例如 Argon2, scrypt, bcrypt, PBKDF2。

加盐 (Salting):
在对密码进行哈希之前,会生成一个随机的、独一无二的字符串(盐),然后将这个盐与密码连接起来,再进行哈希。
例如:`SHA256(password + salt)`。
作用:
防止彩虹表攻击: 彩虹表是预先计算好的哈希值与对应明文密码的查找表。如果所有用户都使用相同的哈希算法,攻击者可以使用彩虹表快速破解大量密码。加盐后,即使两个用户使用相同的密码,他们的哈希值也会因为盐的不同而不同,彩虹表就失效了。
增加计算成本: 每个用户都有一个独立的盐,攻击者无法复用已有的哈希计算结果来破解。

密钥派生函数 (Key Derivation Functions KDFs):
KDFs是专门为密码哈希设计的算法,它们比简单的哈希函数(如MD5、SHA1、甚至SHA256)具有更高的计算复杂度,并且通常允许调整计算成本(迭代次数)。
主要特点:
高计算成本 (Computational Cost): 通过增加迭代次数(例如PBKDF2的`iterations`参数),可以大大减慢破解速度,使暴力破解变得非常困难和昂贵。
内存硬性 (Memory Hardness)(针对某些KDFs如scrypt和Argon2): 这些算法需要大量的内存才能运行,进一步增加了GPU等硬件进行并行破解的难度。
可配置性 (Configurability): 可以根据硬件发展,逐渐增加迭代次数或内存需求来保持安全性。

举例说明更好的做法:

1. 生成一个唯一的盐: 例如,使用 `openssl rand base64 32` 生成32字节的随机盐。
2. 将盐与密码组合: 例如,`password + salt`。
3. 使用安全的KDF进行哈希:
PBKDF2: `PBKDF2(password + salt, salt, iterations=100000, keylen=256, hash_algorithm='sha256')`
bcrypt: `bcrypt(password + salt)` (bcrypt本身包含了盐)
Argon2: `Argon2(password + salt, salt, iterations=100000, memory=65536, parallelism=4)`

存储时,你会存储盐和最终的哈希值(通常将盐嵌入到最终的哈希字符串中,例如 bcrypt)。

验证密码时:

1. 从存储的哈希值中提取盐。
2. 使用相同的KDF、盐和用户输入的密码进行哈希计算。
3. 将计算出的新哈希值与存储的哈希值进行比较。

总结:

将MD5后SHA1进行密码哈希是不推荐的。
MD5和SHA1本身都存在严重的安全漏洞。
组合它们并不能显著提高安全性,反而可能在某些情况下因为依赖不安全的算法而降低安全性。
现代密码安全推荐使用加盐和强大的密钥派生函数(如Argon2, scrypt, bcrypt, PBKDF2)。这些方法通过增加计算成本和抵抗特定类型的攻击来提供更有效的保护。

所以,如果你听到有人建议使用 MD5 后 SHA1 来加密密码,请务必告知他们这是过时且不安全的做法,并建议他们转向更现代、更安全的密码哈希方案。

网友意见

user avatar

文中关于彩虹表的描述有误,评论里比我讲得更准确:


Lu Jerry1 分钟前
关于彩虹表的描述是错误的,这是暴力穷举表,建议答主修改答案。原文“这彩虹表大概多大呢? 答案是, 10TB 的硬盘需要……”修改成“10TB 的硬盘需要一个”。





但凡看了wikipedia, 都不会弄出来这样的问题. 请用 bcrypt/Argon2的较新版本/Scrypt.


我接下来的回答主要针对有些人不明白为什么salt要足够长.


好多人不懂为啥要增加salt的长度, 尤其是 @Ivony 评论底下的.


(建议你们还是直接用相应语言的bcrypt官方demo吧.... Go的bcrypt库十分友好, 知道你不会加盐, 加盐的步骤直接在内部帮你包装好了...【手动狗头】)



我们举个理论模型, 现在用户密码是4位的大小写数字字母符号组合(26大写, 26小写, 10数字, 18符号, 总计80个), 加密方式SHA1(8 bytes), 那么彩虹表有多大呢? 是80^4 x 8bytes = 312.5MiB. SD卡都能装得下.


这时如果我们给用户密码再来个4位的跟密码一样空间的salt, 那么需要的彩虹表会是多大呢? 80^4 x 80^4 x 8bytes = 11.92 EiB 这彩虹表大概多大呢? 答案是, 10TB 的硬盘需要 1300 多块才能装得下, 假设一个 2U 机器装 12 块硬盘, 这需要 6 个机柜(42U计算).


有同学会说了, 那我让用户直接来一个长密码, 16位! 这需要 2.25 × 10^31 byte! 总没问题了吧? 你觉得用户会记得住一个长达16位的密码吗? 他肯定会用手机号/生日随便再来点什么对吧. 那对应的彩虹表也会相当的小. (用户密码熵不可置信问题)


那么有了salt就可以了吗? 不可以, salt也要足够长/足够复杂. 这样才能达到增加 hash target 的信息量(也就是熵)的目的. 这样才会让彩虹表的制作足够难.


设想一个极端场景, 数据库被"拖库"了, 不但hash结果, 甚至salt都已经是已知的了, 源代码也被泄露了, 散列方法无论是 bcrypt 还是 Argon2 还是 Scrypt 人家都知道了, 甚至你的库进行着定期的rehash, 人家连过去的版本也搞到了(可能进行特征/侧信道攻击), 我们该如何保护用户的密码原文?


那就只有salt的熵了(期望用户密码拥有足够的熵不靠谱). 而提升salt的长度可以有效提升存储熵的空间. 这就是 @Ivony 回答论述的主要内容.


所以正确的做法是, 要求用户的密码有足够的熵(复杂度检测, 不通过让他换密码). 以及salt也要有足够的熵. 然后请用相应语言的密码学库里面的散列方法. 或者干脆, 用脑子记住用bcrypt.


试想一下 cracker 去 StackOverflow 问: "各位大佬, 求一个1024bit长度的彩虹表", 会不会被人笑爆然后问, 怎么? 你捡到外星U盘还是外星电脑了? :)




大师 Bruce Schneier 有句话, 被称作 Schneier's Law:


"Anyone, from the most clueless amateur to the best cryptographer, can create an algorithm that he himself can’t break."


人家大师就是大师, 不会直接折了你面子, 但其实意思就是, 别自己发明加密算法.


- 真正安全的算法是久经社区考验的, bcrypt已经22年了(1999-2021), Scrypt也12年了(2009-2021).

- 而且是要社区流行的. 光自己看的懂没意义, 别人也能用才有意义, 才能成为标准.


Schneier 对此又说了:


"There are two kinds of cryptography in this world: cryptography that will stop your kid sister from reading your files, and cryptography that will stop major governments from reading your files."


(世界上有两种密码学: 一种是阻止你妹妹阅读你的文件的密码学, 另一种是阻止强大的政府阅读你文件的密码学.)




最后.


建议多读书, 书上写的很清楚:


"If the salt is big enough, it essentially makes dictionary attacks infeasible."


Secure Programming Cookbook for C and C++


延伸阅读, 现在已经不太建议用PBKDF2了(因为有FPGA/ASIC), 应该转向其他的 GPU/内存/ASIC 抗性更强的散列方法:


medium.com/analytics-vi

user avatar

没有意义,早就有md5+sha1的彩虹表了。


至于说加盐对抗彩虹表的人,也绝大多数不明白这里面的原理和逻辑。

加盐的确是对抗彩虹表,但是到底是怎么对抗的,其实很多人仍然是一知半解。要搞清楚这一点,就要搞清楚彩虹表的原理。

彩虹表很简单,就是预先计算出所有可能密码的哈希值,然后破解的时候查表好了。

彩虹表的方式理论上无法防御(这与加密算法不同),只要是有限长度的密码,就可以被彩虹表攻击。并且彩虹表可以复用,所以计算单次密码攻防成本也是扯淡,随着时间的推移,彩虹表只会增长不会收缩


所以对抗彩虹表只有一个方法,这也是绝大多数人压根儿没有弄明白的事情,彩虹表最大的敌人是密码的可能性,更进一步的如果考虑到彩虹表的无限增长的问题,所以彩虹表的唯一防御手段是增加密码长度!

所以加盐的本质目的是:增加密码长度


密码长度的增加,破解该密码的彩虹表的大小需要呈指数增长,这才是防御彩虹表的根本原因。


所以加盐是必须的,但是你加的盐太短了,把你的盐当作密码的一部分去查表就完了……



其实说白了吧,如果你只是要防御彩虹表,仅仅针对彩虹表防御,你只需要把你的密码拉到特别长,彩虹表直接失效,有没有盐都失效了……

更进一步的,如果你的密码长度足够长,长到比哈希值的长度还要多出不少。这时候即便别人找到了一个碰撞,那大概率也不是你的密码了,你密码的安全可以得到更彻底的保护……


最后再补充一点好了,彩虹表破解的是你的密码,他的依据是大部分人在不同的网站会使用同一个密码,所以得到你的密码,就能入侵别的网站。对于已经侵入后台拿到你密码哈希值的黑客来说,再去伪造你的身份已经没啥意义了,因为他既然都已经侵入了网站的后台,在这个网站想干点儿啥还需要你那密码么?

user avatar

看来你不知道什么是salt。生成一个随机数,我们称之为salt,然后在数据库中记录salt和h=hash(pwd + salt),查询的时候,得到用户的口令p,然后从数据库中查出salt,计算hash(p+salt),看是不是等于h,等于就是对的,不等于就是不对的。

单纯使用MD5之所以不好,并不是说MD5这种方法容易遭到破解,而事实上对于MD5求原象或者第二原象,也就是“逆计算”这种破解,没有什么很好的方法。只能通过预先计算知道许多MD5的对应关系,存在数据库中,然后使用的时候反查,例如我知道'password'的MD5值是5f4dcc3b5aa765d61d8327deb882cf99,那么我就用一个数据库存起来,只要我看到5f4dcc3b5aa765d61d8327deb882cf99,我就知道这个是口令'password‘使用MD5处理之后的值,原来的口令就是'password'。MD5在身份鉴别系统中用于口令保护已经是很久了事情了,大部分黑客也有针对这种Hash方式准备相应的数据库进行反查,这种数据库称为彩虹表。

所以,为了对抗彩虹表,我们要做的工作是避免预先计算,让攻击者无法(或者非常困难)提前计算好彩虹表。

为了反映为何彩虹表计算是可行的,我们再来算一下。我们假设用户可能输入的口令是键盘上的小写字母和数字,共26+10=36种,之所以这样假设是因为 一个用户比较多的系统中总是会有一些弱口令用户的,我们假设输入的口令至少5个字符,至多12个字符,那么用户可能的输入一共有:

,而12个字节可能的组合应有

种。如果再考虑到用户为了方便记忆,输入的口令是一些已经存在的单词或是词组,可能的输入将会远远少于。用户可能的输入少了,就给了我们枚举的空间。

为了阻止这种枚举,加salt的方法是扩大用户输入的一种简单有效的途径,随机生成一个16字节的随机数,加上用户本身输入的至多12个字符的口令,可能的输入就有种,这么多种可能性,任何一个机构和组织都没有办法存储规模如此庞大的彩虹表。

另外一种方法是通过提升Hash的复杂度,延长攻击者进行暴力破解时所消耗的时间。现在显卡用于并行计算实在太容易,6位纯数字的口令在显卡看来就是秒破。Hash算法的多次迭代就是最简单的延长计算时间的方法,Apache的htpasswd就使用了MD5的1000次迭代,不过只是使得这些口令稍微难破解一些。

另外,题中使用了SHA1和MD5两种算法的方法,除了稍微提升一点计算的难度以外,并没有多好,这种组合方法不能增加用户输入的可能性,另外虽然SHA1生成的是160位的Hash,但是由于输入是一个128位的MD5,所以输出也至多只可能有种可能,猜测的范围也没有缩小。所以这是原来回答我建议你使用更多次数(如1000次)MD5迭代的原因,至少应当有一个方面有稍微大一些的加强。

另外

此文(暴力密码破解器 ocl-Hashcat-plus 支持每秒猜测最多 80 亿个密码,意味着什么?

)中有数据可以供参考,bcrypt是一种有效对抗口令Hash破解的算法,建议使用。

类似的话题

本站所有内容均为互联网搜索引擎提供的公开搜索信息,本站不存储任何数据与内容,任何内容与数据均与本站无关,如有需要请联系相关搜索引擎包括但不限于百度google,bing,sogou

© 2025 tinynews.org All Rights Reserved. 百科问答小站 版权所有