服务器帮你验证密码,但它连你密码的哈希值都不知道。OPAQUE 实现了密码安全的圣杯。
前情回顾:SRP 的遗憾
在上一篇中,我们介绍了 SRP 协议。它已经很强大了:
- ✅ 密码不在网络上传输
- ✅ 服务器不知道真实密码
- ✅ 监听者无法窃取密码
但 SRP 有一个隐藏的弱点:服务器存储的"验证器"仍然与密码直接相关。
SRP 存储的验证器:v = g^(H(salt, password)) mod N ← 直接由密码派生
如果数据库泄露:
攻击者获得 salt 和 v 后,可以离线暴力破解:
for password in wordlist:
x = H(salt, password)
v_guess = pow(g, x, N)
if v_guess == v:
print("Found:", password)
break
不需要服务器参与,攻击者可以在自己的机器上无限尝试。虽然离散对数难题增加了计算成本,但这仍是"离线"攻击!
OPAQUE 的野心
OPAQUE(2018年提出,2023年成为IETF标准草案)的目标更加激进:
即使服务器数据库完全泄露,攻击者也无法离线破解密码。
怎么做到的?答案是:服务器自己都不知道验证器是怎么算出来的!
一个脑洞大开的类比
想象一个特殊的"蒙眼锁匠"场景:
背景设定
你有一个保险箱,钥匙的制作方法很特殊:
- 钥匙 = 你的秘密配方 + 锁匠的独门手法
- 两者缺一不可
SRP 方案的问题
你用配方自己做了钥匙,把钥匙的"形状参数"告诉锁匠存档。
问题:如果锁匠的档案室被盗,小偷拿到"形状参数"后,可以在自己家里疯狂尝试各种配方,看哪个能做出相同形状的钥匙。每秒试十亿次,总能试出来。
OPAQUE 方案
制作钥匙的过程:
- 你:把配方变成一团神秘材料(你自己也不认识了)
- 你 → 锁匠:传递神秘材料(双方都不知道里面是什么)
- 锁匠:用独门手法加工材料(不知道原料是什么)
- 锁匠 → 你:传回加工后的材料
- 你:去掉神秘外壳,得到最终钥匙 = 配方 + 手法
结果:
- 你知道配方,但不知道手法
- 锁匠知道手法,但不知道配方
- 钥匙 = 配方 + 手法,双方都无法独立制作!
如果小偷偷了锁匠的档案室?
小偷偷到了什么?
锁匠的档案室:
- 用户 Alice 的 “加工后材料”
- 用户 Bob 的 “加工后材料”
- …
但是:这些"加工后材料"已经混合了锁匠的手法,没有手法,就无法还原出原始配方!
小偷想暴力破解:
- 遍历所有可能的配方
- 把配方"神秘化"
- 加工?→ 需要锁匠的手法!无法继续!
每次尝试都必须请锁匠帮忙加工,但锁匠会记录尝试次数,超过限制就锁定账户!
对比:
| 方案 | 破解方式 | 速度 |
|---|---|---|
| SRP | 离线破解 | 每秒 10 亿次 → 弱密码几分钟破解 |
| OPAQUE | 在线破解 | 受服务器限流(每分钟 5 次)、3 次失败锁定账户 |
这就是 OPAQUE 的革命性突破:把暴力破解从"离线"逼到"在线"。
OPAQUE 的两个关键技术
1. OPRF:双盲计算
OPRF(Oblivious Pseudo-Random Function,不经意伪随机函数)是 OPAQUE 的核心黑科技。
普通哈希 (如 Argon2):
- 输入: password
- 输出: H(password)
- 特点: 任何人都能算,只需要知道密码
OPRF:
- 输入: password (客户端持有) + key (服务器持有)
- 输出: F(key, password)
- 特点:
- 客户端不知道 key → 无法独立计算
- 服务器不知道 password → 不知道在计算什么
- 双方合作才能得到结果
神奇之处:服务器帮你计算,却不知道你算的是什么!
这听起来像悖论?让我解释:
- 客户端盲化:
blind = H(pwd)^r(用随机数 r 遮住密码) - 客户端 → 服务器:发送
blind - 服务器计算:
result = blind^key - 服务器 → 客户端:返回
result - 客户端去盲化:
output = result^(1/r) = H(pwd)^key
数学魔法:
- blind = H(pwd)^r
- result = blind^key = H(pwd)^(r*key)
- output = result^(1/r) = H(pwd)^key ← r 被消掉了!
结果:
- 客户端得到 H(pwd)^key
- 服务器只看到了 blind (被 r 遮住的值)
- 服务器无法从 blind 推出 password
- 每次的 r 都不同,服务器无法关联多次请求
这就像一个神奇的翻译机器:
- 你把密码用密码本 A 加密后发过去
- 服务器用它的密码本 B 再加密一次后发回来
- 你用密码本 A 解密
- 最终结果是只用密码本 B 加密的密码
- 但服务器从没见过解密后的密码!
2. AKE:认证密钥交换
在 OPRF 的基础上,OPAQUE 使用 AKE(Authenticated Key Exchange)完成:
- 双方相互认证身份
- 建立加密的会话密钥
- 防止中间人攻击
OPAQUE 完整流程
注册阶段
Step 1-2: 客户端准备
- 生成客户端密钥对
(pk_c, sk_c) - OPRF 盲化:
blind = H(pwd)^r
Step 3: 客户端 → 服务器 发送 blind, pk_c
Step 4: 服务器计算
- OPRF 计算:
response = blind^key - 生成服务器密钥对
Step 5: 服务器 → 客户端 返回 response, pk_s
Step 6: 客户端处理
- 去盲化得到
rwd(随机密码 wrapper) - 加密客户端密钥:
envelope = Enc(rwd, sk_c)
Step 7: 客户端 → 服务器 发送 envelope
Step 8: 服务器存储
envelope(加密的客户端私钥,用 rwd 加密)oprf_key(OPRF 私钥)pk_c(客户端公钥)
关键点:服务器不知道 rwd,因为它无法从 blind 推出 password,所以服务器无法解密 envelope!
登录阶段
Step 1: 客户端准备
- OPRF 盲化:
blind = H(pwd)^r
Step 2: 客户端 → 服务器 发送 username, blind
Step 3: 服务器处理
- 查找用户
- OPRF 计算:
response = blind^key
Step 4: 服务器 → 客户端 返回 response, envelope
Step 5: 客户端处理
- 去盲化:
rwd = F(key, pwd) - 解密:
sk_c = Dec(rwd, envelope)
Step 6-7: 双向认证
- 客户端用
sk_c进行认证密钥交换 - 服务器验证并建立会话
- 双方建立安全会话
结果:
- 双方建立安全会话
- 密码从未传输
- 服务器从未知道密码
- 服务器也无法解密 envelope (因为不知道 rwd)
如果密码错误:
rwd' = F(key, wrong_pwd) ≠ rwdDec(rwd', envelope) = 乱码- AKE 验证失败 → 登录失败
为什么 OPAQUE 能防止离线破解?
关键在于:暴力破解必须在线进行。
假设攻击者偷到了服务器数据库:
攻击者获得:
- envelope (加密的客户端私钥)
- oprf_key? 这取决于部署方式:
- 最佳实践:存储在 HSM(硬件安全模块)中,数据库泄露不会暴露
- 一般部署:可能存储在配置文件或内存中,需要额外的保护措施
攻击者想暴力破解:
for password in wordlist:
blind = pow(H(password), r, N)
response = ??? # Need server's oprf_key!
rwd = unblind(response)
if decrypt(rwd, envelope):
print("Found!")
卡在第 3 步:无法独立计算 OPRF 结果,需要服务器的 oprf_key!
攻击者的唯一选择:假装正常用户,向服务器发起登录请求
for password in wordlist:
response = server.login(username, blind) # online request
if login_success:
print("Found!")
但是:
- 每次请求都有网络延迟 (~100ms)
- 服务器会限流 (每分钟 5 次)
- 连续失败会锁定账户
- 服务器会记录并报警
1000 个密码需要 3+ 小时,并触发安全警报。对比离线攻击:1000 个密码只需 0.00001 秒。
这就是"离线变在线"的威力!
这就是 OPAQUE 的革命性突破:把暴力破解从"离线"逼到"在线"。
SRP vs OPAQUE 终极对比
| 特性 | SRP | OPAQUE |
|---|---|---|
| 密码不传输 | ✅ | ✅ |
| 服务器不知道密码 | ✅ | ✅ |
| 防网络监听 | ✅ | ✅ |
| 防服务器数据库泄露 | ⚠️ 可离线破解 | ✅ 只能在线破解 |
| 实现复杂度 | 中等 | 较高 |
| 标准化程度 | RFC 5054(较老) | IETF 草案(最新) |
| 量子计算抵抗 | ❌ | 可升级 |
OPAQUE 的代价
天下没有免费的午餐,OPAQUE 的强大安全性也有代价:
- 实现复杂:需要实现 OPRF,比 SRP 更复杂
- 计算开销:每次登录需要额外的 OPRF 交互
- 生态不成熟:相比传统方案,库和工具较少
- 服务器成本:每次登录都需要服务器参与计算
谁在使用 OPAQUE?
虽然 OPAQUE 还比较新,但已经开始被采用:
- WhatsApp:端到端加密密钥的备份恢复
- Cloudflare:探索用于身份验证服务
- Signal:考虑用于账户恢复
- 企业安全系统:高安全需求场景
未来展望
OPAQUE 代表了密码认证的未来方向:
安全性演进时间线:
| 年代 | 方案 | 安全特点 |
|---|---|---|
| 1990s | 明文存储 | 裸奔 |
| 2000s | 加盐 MD5/SHA | 彩虹表防护 |
| 2010s | Argon2 | 慢哈希 + 内存硬化 |
| 2015+ | SRP | 密码不传输,但仍可离线破解 |
| 2020+ | OPAQUE | 防离线破解,服务器零知识 |
发展趋势:
- 从"存储安全"到"传输安全"再到"计算安全"
- 从"被动防御"到"主动限制攻击面"
- 从"信任服务器"到"零知识/最小权限"
更远的未来可能是:
- 无密码认证:生物识别 + 硬件密钥
- 去中心化身份:区块链 + 零知识证明
- 后量子安全:抵抗量子计算机的算法
技术细节(给好奇的读者)
OPRF 的数学原理
OPRF 基于椭圆曲线密码学:
椭圆曲线上的 OPRF:
设置:
- G = 椭圆曲线上的生成点
- k = 服务器私钥 (随机标量)
- H = 哈希到曲线的函数
协议流程:
客户端:
- 生成随机标量
r - 密码映射到曲线点:
M = H(password) - 盲化(标量乘法):
M' = r × M - 发送
M'给服务器
服务器:
- 用私钥计算:
Z = k × M' = k × (r × M) = r × (k × M) - 返回
Z给客户端
客户端:
- 去盲化:
N = (1/r) × Z = (1/r) × r × (k × M) = k × M(r 被消掉了!) - 输出:
H'(password, N)作为最终的 OPRF 输出
安全性保证:
- 服务器只看到 M’ = r×M,r 是随机的,所以 M’ 泄露不了 M
- 客户端得到 k×M,但不知道 k
- 只有双方合作,才能计算出正确结果
为什么服务器数据泄露不能离线破解?
OPAQUE 的分层防护:
第一层:OPRF 密钥保护
oprf_key (k) 的存储方式(按安全级别排序):
| 方案 | 说明 | 安全级别 |
|---|---|---|
| HSM (硬件安全模块) | 密钥永不离开硬件,所有 OPRF 计算在硬件内完成 | 最高 |
| 隔离进程 + 内存保护 | 使用 Intel SGX / ARM TrustZone | 高 |
| 加密配置文件 | 密钥用主密钥加密存储 | 中 |
| 内存中保存 | 启动时加载,不持久化到磁盘 | 基础 |
注意:OPAQUE 的安全性很大程度上取决于 oprf_key 的保护程度。如果 oprf_key 与 envelope 一同泄露,攻击者可以离线破解(虽然计算成本仍高于传统哈希)。使用 HSM 可以获得最强保护。
第二层:envelope 保护
即使攻击者拿到 oprf_key,也无法直接破解:
envelope = Encrypt(rwd, client_secret_key)rwd = OPRF(k, password)← 攻击者不知道 password!
攻击者必须:
- 猜测 password
- 计算 OPRF(k, guessed_password)
- 尝试解密 envelope
- 验证解密结果是否正确
每次猜测都需要完整执行这 4 步,计算成本远高于传统哈希。
第三层:在线限制
如果 oprf_key 在 HSM 中:
- 每次 OPRF 计算需要调用 HSM
- HSM 有物理速率限制(如 1000 次/秒)
- 暴力破解被硬件限速
如果攻击者只有 envelope:
- 必须向服务器发起真实登录请求
- 受网络延迟和服务器限流约束
- 每秒最多几十次,而非几十亿次
总结
OPAQUE 实现了密码安全的"圣杯":
密码的最高保护等级:
- 密码不在网络传输
- 服务器不知道密码
- 服务器不知道密码的哈希
- 数据库泄露无法离线破解
- 暴力破解只能在线进行(可被限流/锁定)
核心技术:OPRF (不经意伪随机函数)
F(k, password) 需要双方合作计算:
- 客户端知道 password,不知道 k
- 服务器知道 k,不知道 password
- 单方无法独立计算结果
安全性对比:
| 方案 | 数据库泄露后的破解难度 |
|---|---|
| MD5 | 秒级(彩虹表) |
| Argon2 | 小时~天级(GPU 暴力破解) |
| SRP | 天~周级(计算量更大) |
| OPAQUE | 不可行(必须在线,被限流) |
它的核心魔法是 OPRF——一种让服务器参与计算、但又不知道计算内容的神奇技术。
虽然 OPAQUE 目前还不像传统密码方案那样普及,但它代表了密码认证的未来。随着网络安全威胁越来越严峻,我们可能会看到越来越多的服务采用 OPAQUE 或类似的先进协议。
毕竟,最好的密码保护方式,就是让任何人(包括你信任的服务器)都接触不到你的密码。
系列总结:密码安全的进化之路
时间线:
| 年代 | 方案 | 特点 |
|---|---|---|
| 1990s | 明文存储 | 完全裸奔 |
| 2000s | MD5 + 盐值 | 可离线破解 |
| 2010s | Argon2 | 破解变慢 |
| 2020s | SRP | 密码不传输 |
| 未来 | OPAQUE | 无法离线破解 |
各方案特点对比:
| 特性 | 明文 | MD5 | Argon2 | SRP | OPAQUE |
|---|---|---|---|---|---|
| 服务器知道密码 | 知道 | 知道哈希 | 知道哈希 | 不知道 | 不知道 |
| 密码传输 | 是 | 是 | 是 | 否 | 否 |
| 离线破解 | 直接看 | 秒级 | 小时级 | 天级 | 不可行 |
| 实现难度 | 无 | 低 | 低 | 中 | 高 |
选型建议:
| 场景 | 推荐方案 |
|---|---|
| 一般 Web 应用 | Argon2id(够用,生态成熟) |
| 高安全需求(金融、医疗) | SRP 或 OPAQUE |
| 终极安全需求 | OPAQUE + 硬件密钥 |
密码安全没有银弹,但选择正确的方案,能让攻击者的成本从"几秒钟"变成"几千年"。
本系列:
- MD5:一部血泪史
- Argon2:慢哈希的艺术
- SRP:证明你知道密码却不说出密码
- OPAQUE:防离线破解的终极方案(本篇)
延伸阅读:零知识证明、后量子密码学、WebAuthn 无密码认证