密码学基础回顾:哈希、加密、HMAC 与 KDF
做盲索引之前,我先把密码学基础拉一遍。你别觉得这些是小儿科——在实际项目中,索引泄漏的坑,九成以上都出在基础认知不牢上。我个人习惯是,每次设计新协议前,先拿这四个工具在脑子里过一遍:能用谁、不能用谁、组合起来有什么副作用。
1. 哈希函数:单向道的核心
哈希函数是个黑盒子。你扔进去任意长的数据,它吐出一个固定长度的摘要。关键特性就两条:抗原像性和抗碰撞性。
- 抗原像性:从摘要反推原文,计算上不可行。
- 抗碰撞性:找不到两个不同的输入,让它们哈希值相同。
常用的有 SHA-256、SHA-3。MD5 和 SHA-1 已经被撞破了,千万别再用。
哈希函数的输出是不可逆的。这在盲索引里意义重大:我们把原始标识符,比如用户ID或文档哈希,作为查询的基底。服务器只知道哈希值,不知道原文。但要注意,单靠哈希不够——如果枚举空间小,比如手机号或身份证号,攻击者可以自己算一个彩虹表,直接查表还原。
2. 对称加密:保密性第一
对称加密大家都不陌生。加密和解密用同一个密钥。常见的算法有 AES、ChaCha20。
AES 是目前的标准,密钥长度支持 128、192、256 比特。模式上,ECB 不要用——同样的明文块加密结果相同,模式泄漏非常致命。我个人建议用 GCM 或 CTR 模式,自带认证或作为流密码使用。
| 模式 | 安全性 | 推荐场景 | 我踩过的坑 |
|---|---|---|---|
| ECB | 低,模式泄漏 | 不推荐 | 图像加密后轮廓可见,明文边界一目了然 |
| CBC | 中等 | 文件加密 | 填充预言攻击需额外注意 |
| GCM | 高,自带认证 | 网络传输、盲索引查询 | nonce 必须唯一,重复即泄露 |
| CTR | 高,流密码 | 高性能场景 | 同样nonce不能复用 |
在盲索引里,对称加密常用于加密索引的叶子节点。你想想看,如果索引项是密文的,服务器即使拿到数据,也看不出内容。但密钥管理就成了新的挑战——这个我们后面会讲。
3. HMAC:防篡改的标签
HMAC 是哈希函数的升级版。它引入了一个 密钥,把密钥和消息揉在一起再哈希。这样输出的标签,只有持有密钥的人才能验证。
为什么要用密钥?因为哈希函数本身是公开的,谁都能算。加上密钥后,同时保证了完整性和真实性。消息在传输中被篡改,HMAC 校验一定会失败。
// 伪代码示例
key = "my-secret-key"
message = "user-123"
hmac_result = HMAC-SHA256(key, message)
// 输出:a3f2b8c1d9e0...
在盲索引的构造中,HMAC 特别适合用来做伪随机化。举个例子:用户的邮箱作为查询条件。如果直接用哈希,攻击者可以枚举常见邮箱。用 HMAC 加一个只有服务端知道的密钥,枚举就没用了——因为你不知道密钥,算不出正确的 HMAC 值。
4. 密钥派生函数:从密码生成密钥
KDF 解决的问题很简单:用户输入一个密码,系统需要得到一个密钥。但密码往往熵很低,直接哈希容易被暴力破解。KDF 通过 慢哈希 和 加盐,大幅提高暴力破解成本。
常见的 KDF:
- PBKDF2:基于密码和盐,反复迭代哈希。迭代次数越高越安全。
- scrypt:除了迭代,还占用大量内存,对抗硬件攻击效果好。
- Argon2:目前最推荐,2015 年密码竞赛获胜者,能同时控制 CPU 和内存开销。
// PBKDF2 示例(Python)
import hashlib
import os
salt = os.urandom(16)
dk = hashlib.pbkdf2_hmac('sha256', b'my_password', salt, 100000)
print(dk.hex())
在盲索引体系里,KDF 最常见的用法是派生索引加密密钥。客户端从用户口令派生出一个子密钥,用来加密或计算索引值。这样的话,即使服务端的索引库被拖走,没有用户口令,攻击者也无法解密任何数据。
这里有个隐藏的坑:盐必须存储在客户端。如果盐存服务端,每次查询都要先取盐,那就多了一次网络交互,而且盐本身也成了攻击面。我习惯把盐直接写在用户设备的本地存储里,查询时直接拿到。
5. 核心知识体系一览
下面这张图,把四种工具的关系和分工画清楚了。你看一眼,心里就有数了。
你看,四种工具各有各的位子。哈希做基础隐藏,加密做内容保护,HMAC 做认证,KDF 解决密钥派生。盲索引的核心思路,就是把它们组合起来,做到查询过程不泄漏任何原始信息。
6. 实战避坑清单
说了这么多,我总结几个实战里最容易翻车的点:
- 别用自定义哈希。不管你觉得自己多聪明,用 SHA-256 总是没错的。
- 密钥管理是最薄弱的环节。我曾经见过把 AES 密钥直接硬编码在代码里的项目——那还不如不加密。
- HMAC 不能代替数字签名。HMAC 是对称的,掌握密钥的人可以伪造消息。在多方场景下,你需要非对称手段。
- KDF 的迭代次数不能太少。PBKDF2 至少 10 万次迭代,Argon2 的时间成本参数要设到 3 以上。
- nonce 或 IV 必须唯一。在 GCM 模式下,nonce 重用直接导致密钥流泄漏,加密等于废纸。