FIDO U2F 工作原理(配合 Yubikey)

U2F 是一种使用硬件完成二次验证的协议,尝试解决过去的二次验证(2FA)或者多次验证(MFA)中的问题,比如对钓鱼网站没有防治功能1;当然更能避免短信验证中运营商劫持等问题。

我们先假设用户使用硬件是 Yubikey,要在一个浏览器中先后完成 2FA 的启用和验证。这个过程可以在 Yubico demo website 上模拟。

启用过程2

首先,用户请求在一个网站上启用 U2F,则网站的 server 至少会给前端返回:

  1. challenge,一个随机值,比如 "gcgeFFWFSxlXcCsGXmB0dY1kTeVx3rL/ZqN+Gjh2Vns="
  2. app id,一个代表网站的 URI3。现在也叫 rp id,即 replying party id,这其实是 WebAuthn 协议中使用的名称,但和 U2F 的 app id 类似4

然后,前端通过浏览器 API 把上述数据发送给 yubikey,所以需要浏览器支持 U2F。不过,目前 Chrome、Firefox 等浏览器已经移除了独立的 U2F API56,U2F 改为通过 WebAuthn API 实现5navigator.credentials.create()

另外,浏览器负责检查 app id 和当前网站是否一致,以防止钓鱼网站,然后再调用 yubikey 硬件。发给硬件的数据包括(其中的 origin 约等于 http 协议中的 origin):

  1. origin 哈希:sha256(origin)
  2. challenge 哈希:sha256(challenge, origin, 其他)

接着,Yubikey 生成公私钥、key handle(私钥提手)、签名、设备证书(attestation cert),并返回给前端。

其中 key handle 的作用是方便 yubikey 找到特定的私钥,因为一个 yubikey 会生成很多个私钥,对不同的网站会生成不同的私钥。yubikey 对于 key handle 的算法是 keyHandle = AES(f(master key), private key + origin 哈希),也就是说私钥经过加密后,是返回给网站去存储的,这样的好处是 yubikey 不用自己记录私钥,节省存储空间,而且 yubico 认为这样并没有不安全2

最后就是网站 server 收到前端转发来的签名信息,然后校验签名。成功,则把公钥、key handle 存储在用户的账户信息中。至此就完成了 U2F 的启用。

U2F registration process

验证过程2

  1. 用户进入 U2F 验证的步骤,则服务器给前端返回验证需要的信息:key handle,app id,challenge。这些概念在“启用过程”中都出现过。
  2. 前端通过浏览器 API navigator.credentials.get() 调用 yubikey。浏览器检查当前网页 origin 和 app id 是否匹配。检查通过则把 origin 哈希,challenge 哈希,key handle 发给 yubikey.
  3. yubikey 利用 key handle 找到之前用于这个网站的私钥。基于 yubikey 的 key handle 算法,其实就是从 key handle 中解密出私钥和刚启用时使用的 origin 哈希,比对当前验证的 origin 哈希和启用时的 origin 哈希。
  4. yubikey 签名:signature(challenge 哈希 + origin 哈希 + counter),并把签名、counter 返回给浏览器
  5. 前端转发签名信息给 server,server 用公钥校验签名,并且比对返回的 counter 大于之前存储的 counter。至此完成一次验证。

U2F authenticatoin process

其他硬件,其他 app

对于其他非 yubikey 的密钥硬件,U2F 协议的表现也是相同的,只是硬件返回给 server 的数据会有细节上的差别,比如 key handle 的算法不同。

对于非浏览器的 app,可能要考虑怎么组织自己的 app id,比如在一个 app id 中使用多个 facet id 代表不同的设备平台7。但 U2F 协议的其他表现也是一样的。

有什么用

这种协议设计如何保护我们的数据安全呢?

我们分别假设整个流程中,不同的组件被攻击。

  1. 弱密码或密码泄漏:各种 2FA 技术都能阻止单纯的密码泄漏导致的攻击,U2F 当然也能
  2. 服务器被攻击:你在这个网站的数据极可能会泄漏,但攻击者无法得到你的 FIDO master key(存在 yubikey 中),所以就无法破译你在其他网站的 U2F key handle,那些账户不会受到影响。
  3. 网站被模仿:攻击者用一个域名和页面都很像的网站钓鱼你,他会得到你输入的用户名和密码,但是浏览器只会使用钓鱼网站的 origin 让 yubikey 签名。当攻击者想用你刚刚生成的签名登录真网站时,origin 对不上则签名验证会失败,无法窃取你更多的数据。
  4. 浏览器或者 app 被篡改:比如你从不可信的地方下载的应用软件,其代码可能已经被篡改,会跳过 origin 校验,这时 yubikey 签名用的 origin 和 challenge 可能都来自真网站,你的签名就是有效的,攻击会就能冒充你完成登录。甚至,攻击者都不用在乎你的 U2F 验证过程,只需要等你彻底完成登录之后,拿走你的 Session Token 或者 Access Token 就行。
  5. 连接不安全:如果连接本身不安全,中间人可以得知实际的 challenge 和对应的签名,也可以窃取你的 Session Token 继而模仿你的身份。
  6. yubikey 被复制:据说,部分型号的 yubikey 也可能被复制,但是需要物理破坏硬件的外壳1,因此买到 yubikey 之后应该检查一下外观。不过,外观修复可能并不容易8,广撒网式大批量伪造的可能性会降低。当然或许还有制造商本身作恶的风险1,比如生产时记录 master key。不过对于二次验证的场景,如果攻击者不知道你的用户名和密码,也就拿不到数据。

综上,U2F + yubikey 也并不能防止所有的安全问题。但是能辅助网站确认你是你,在安全的设备和浏览器里也能防止钓鱼。

1. Universal 2nd Factor - Wikipedia
2. How FIDO U2F works
3. App ID
4. Migrating from U2F
5. Google Chrome U2F API decommission: What the change means for your users and how to prepare - Yubico
6. 1737205 - Remove the U2F DOM API once Chrome and Google websites have deprecated it
7. Universal 2nd Factor (U2F) Overview
8. Should you worry about hackers cloning your 2FA hardware security keys? | ZDNET