常见哈希算法

MD5:32 位,单向哈希,不可逆,速度快,破解难度低

SHA256:256 位,单向哈希,不可逆,速度较快,破解难度中等

BCrypt:可变位数,单向哈希,不可逆,速度慢,破解难度高

PBKDF2:可变位数,单向哈希,不可逆,速度可调,破解难度可调

Scrypt:可变位数,单向哈希,不可逆,速度慢,破解难度高

加盐,在输入信息中随机添加字符串(salt)以提高哈希算法的安全性

MD5 算法

MD5 消息摘要算法(英语:MD5 Message-Digest Algorithm),一种被广泛使用的密码散列函数,可以产生出一个 128 位(16 个字符 (BYTES))的散列值(hash value),用于确保信息传输完整一致

MD5 算法存在隐患(无法阻止碰撞攻击),因此不适用于安全性认证

MD5 输入是不定长度信息,输出则是固定长度 128-bits

MD5("The quick brown fox jumps over the lazy dog")
= 9e107d9d372bb6826bd81d3542a419d6
# 实际输出为32位的16进制数据

MD5 计算过程(图源):

  • 先填充(第一位填充1,其余位填充0)再分为 N 组
  • 每组划分为 16 个子分组,分别进行 FF、GG、HH、II 运算 4 次
  • 其中 FF 运算过程为:a = b + ( (a + F (b, c, d) + Mj + ti) <<< s)
  • a,b,c,d 为随机初始化的 32bit 的 16 位字符串,其在计算过程中不断更新,以累积输入的信息,总共迭代计算 $64=16\times 4$ 次;Mj 为当前子分组数据;ti 为常量;s 为左移的位数,也是常量
  • GG、HH、II 运算过程是将 F 函数替换为 G、H、I 函数,其他不变
  • 最后将在子分组计算更新后的 a,b,c,d 拼接,得到最终的 MD5

由于计算过程均为位运算,因此 MD5 的计算非常快

其中的 F 、G、H、I 函数对应公式如下: $$ \begin{aligned} &F(X,Y,Z)=(X\wedge Y)\vee(\neg X\wedge Z) \\ &G(X,Y,Z)=(X\wedge Z)\vee(Y\wedge\neg Z) \\ &H(X,Y,Z)=X\oplus Y\oplus Z \\ &I(X,Y,Z)=Y\oplus(X\vee\neg Z) \end{aligned} $$

  • $\oplus$,$\vee$,$\wedge$,$\neg$ 分别表示 XORANDOR_ , _NOT

SHA 算法

SHA(安全哈希算法,Secure Hash Algorithms),是由美国联邦信息处理标准 (FIPS) 发布的一系列加密哈希算法

  • SHA-0:1993年发布,160 位,因安全隐患被撤回
  • SHA-1:1995年发布,160 位,类似早期的 MD5 算法
  • SHA-2:2001年发布,包含 SHA-256 和 SHA-512 两个系列
  • SHA-3,2015年发布,哈希长度与 SHA-2 类型,但内部结构不同

目前建议使用 SHA-2 或 SHA-3 中的 256、384、512

SHA-256 输出一个256位(32字节)的哈希值,通常以64 位 16 进制表示

类似于 MD5,SHA 算法也需要填充、分组和计算

SHA256 算法用到了 8个哈希初值以及64个哈希常量(细节略)

Bcrypt

Bcrypt 是一种根据 Blowfish 加密算法所设计的密码散列函数。BCrypt 可以通过工作因子(work factor)来调整哈希计算的复杂度,这个工作因子随着时间的推移可以增加,以抵御硬件性能的提升

PHP7 以上版本都默认使用该方法来处理密码

Bcrypt 算法生成的值由 4 部分组成:

  • 2a 表示 Bcrypt 算法;salt 为一个 128bits 随机字符串
  • Ounds:是轮值因子,代表执行 hash 的次数,数值越高越安全,默认是10
  • Hash: 经过明文密码加盐后进行 hash 得到的值(可变位数)

Blowfish 加密算法过程概述:

  1. 密钥初始化:通过密钥扩展过程将密钥(可以是任意长度从 32 位到 448 位)转换成一系列子密钥。这些子密钥会在后面的加密过程中使用(需要大量的计算)
  2. 分块处理:将加密的数据分割成固定大小的块(每块 64 位,不够就填充)
  3. Feistel 网络:Feistel 网络包含了多轮的加密过程,每一轮都会使用一个子密钥。在每一轮中,数据块被分为两半,然后进行一系列的运算(比如替换和置换操作)
  4. 输出结果:经过多轮 Feistel 网络的处理后,最终的结果再次组合成一个 64 位的块,这就是加密后的数据块。然后,算法会继续处理下一个数据块,直到所有的数据都被加密
  5. 解密过程:使用相同的子密钥(但顺序相反),通过 Feistel 网络的逆过程来恢复原始数据

Bcrypt 算法就是对字符串进行64次 blowfish 加密得到的结果

PBKDF2

基于密码的密钥导出函数(Password-Based Key Derivation Function)

PBKDF2 将伪随机函数(例如基于哈希的消息身份验证代码 HMAC)应用于输入密码或密码短语以及盐值,并多次重复该过程以生成派生密钥,然后可以将其用作加密密钥在后续的操作中。增加的计算工作使密码破解变得更加困难,这被称为密钥拉伸(key stretching)

PBKDF2 的迭代次数越多,攻击者尝试破解密码所需的时间就越长

2023 年,OWASP(全球性安全组织) 建议对 PBKDF2-HMAC-SHA256 使用 600,000 次迭代,对 PBKDF2-HMAC-SHA512 使用 210,000 次迭代

Scrypt

Scrypt 设计时考虑到大规模的客制硬件攻击而刻意设计需要大量内存运算,该算法需要产生大量伪随机性(pseudorandom)资料作为计算的基础

Scrypt 算法会产生一个 p(十万~百万)个块元素的数组,对于每个块元素,都进行一系列复杂运算生成的哈希值,最后对整个数组再进行 PBKDF2-HMAC-SHA256 运算得到最终结果

往年同期文章