Caloogy 文章
English

Passkey:为什么它天生防钓鱼

日期
2025 年 12 月 17 日
系列
Web 安全
阅读
3 分钟
主题
PasskeyWebauthnFido2Phishing

一句话版本

密码和短信验证码的问题在于:它们是可以被抄走的秘密,用户在假网站上照样会输进去。Passkey(WebAuthn / FIDO2)换了一种思路:设备里存一把私钥,登录时只对服务器发来的随机挑战签名,而且签名里写明了"我是在哪个网站签的"。 假网站拿到的签名只对假网站有效,转手到真网站就作废。所以 passkey 不是"更难被骗",而是结构上骗不走。

全文分三层:

  1. 通俗理解:钥匙和锁,以及为什么验证码会被骗走
  2. 实际用处:谁在用、钓鱼攻击的真实数据、"敏感操作再验一次"
  3. 原理与工程要点:注册与登录流程、origin 绑定、签名计数器

第一层:通俗理解

1.1 密码为什么总出事

方式 本质 怎么被偷
密码 你和网站共享的一个秘密 撞库、钓鱼页、键盘记录、网站数据库泄露
短信验证码 一次性的共享秘密 钓鱼页实时转发、SIM 卡劫持、社工客服
App 验证码(TOTP) 由共享种子算出的一次性秘密 钓鱼页实时转发

它们的共同弱点是:你知道的东西,你也能告诉别人。钓鱼网站长得和真的一模一样,你输进去,它在后台立刻转发给真网站,整个过程不到一秒。

1.2 Passkey:你不用"知道"任何东西

Passkey 更像一把只能在正确的锁上转动的钥匙:

最关键的一点:签名之前,浏览器会把当前网站的域名写进被签名的内容。你在 examp1e.com(假网站)上按了指纹,签名里写的就是 examp1e.com,拿去真网站 example.com 验证,一定失败。


第二层:实际用处

2.1 敏感操作再验一次(step-up authentication)

登录一次之后,会话可能持续好几天。如果别人借用你的电脑,或者会话 cookie 被偷,他就能以你的身份操作。所以高价值操作(转账、改收款地址、改通知邮箱)应当当场再要一次 passkey。银行转账时再按一次指纹,就是这个思路。

Caloogy Wallet 也采用了这一做法:转账等敏感操作要求 passkey 确认。


第三层:原理与工程要点

3.1 注册(Registration)

网站服务器浏览器用户设备(认证器)网站服务器浏览器用户设备(认证器)随机 challenge + 网站 ID(rpId)生成一对新密钥(只给这个 rpId 用)公钥 + credential ID + 签名连同 clientDataJSON(含 origin)一起发送校验 challenge、origin,存下公钥

3.2 登录(Authentication)

服务器发一个新的随机 challenge。设备签名的内容是:

sig=Signsk(authenticatorData ∥ SHA256(clientDataJSON)),\text{sig} = \mathrm{Sign}_{sk}\big(\text{authenticatorData} \,\|\, \mathrm{SHA256}(\text{clientDataJSON})\big),

其中 clientDataJSON 里包含 challenge、origin(如 https://example.com)和类型;authenticatorData 里包含 rpId 的哈希、"用户在场 / 已验证"标志位和签名计数器。签名算法通常是 P-256 上的 ECDSA(见《椭圆曲线》)。

服务器必须检查:

检查项 防什么
challenge 是自己刚发出的、只用一次 重放旧签名(见《签名登录与防重放》)
origin 和 rpId 是自己的域名 钓鱼:假网站拿到的签名对不上
用户已验证(UV)标志位 确认真的按了指纹 / 输了 PIN,而不只是"设备在场"
签名对得上存下的公钥 冒充
签名计数器递增 检测硬件密钥被克隆(同步型 passkey 计数器常为 0,需要兼容)

3.3 为什么服务器泄露也不怕

服务器只存公钥。数据库被拖走,攻击者拿到一堆公钥,既不能登录,也不能撞库。这一点和密码(哪怕是加盐哈希)有本质区别。

3.4 工程上的坑


总结

层次 要点
直觉 没有可以输入的秘密,自然没有可以被骗走的秘密
原理 公钥签名 + 签名内容绑定 origin;服务器只存公钥
用法 登录之外,敏感操作再验一次(step-up)
坑 找回流程、同步账号的安全、不能降级到短信