我真的很喜欢U2F提供的安全级别,但是除了安全性,还需要考虑恢复计划。 如果主要的U2F令牌出了点问题,则无法访问最重要的帐户是一个严重的问题。 同时,我想避免使用会损害U2F提供的安全性的备份。
流行的备份方法
迄今为止,保留第二个独立的U2F令牌进行备份是一种好习惯。 此令牌必须手动添加到每个服务,并存储在“安全”位置。 另一种常见的做法是使用非U2F方法作为备份(OTP,恢复代码)。 坦白地说,这两种方法都有很多不足之处。
独立的U2F代币
这意味着每次我注册某些新服务时,都需要添加两个令牌。 这个事实引起了许多问题:
- 备份令牌应该相当容易访问。 尽管事实上我不会随身携带钥匙扣,但我应该能够很快找到它,因此我很难想出比在家中更好的东西。 它有多真实,即使使用了保险箱,您也可以长时间交谈。
- 当我必须在出门在外时注册服务时,我无法添加备用令牌。 因此,您需要记住,以后需要添加它,直到发生这种情况,才不会进行备份。 在最坏的情况下,我完全可以忘记他。
- 当我在家时,我的两个令牌都在同一个地方。 这种备份方法远非理想:两个令牌可能由于一个事件(被破坏或被盗)而不可用;
- 备份令牌存储在家里这一事实是完全显而易见的。 如果某人真的想获取我的令牌,他已经知道在哪里寻找它。
- 非通用方法:并非所有服务都允许您向帐户添加多个密钥。
我认为,这种“示范性做法”不是很可靠,而且很繁重。 让我们看看另一种常见的做法。
非U2F方法作为备份
OTP:
- 使用OTP作为备份比使用它作为主要的2FA方法更好,但是使用OTP的事实在某种程度上打开了额外的攻击载体;
- 电话坏了,会丢失并被盗,如果丢失后电话有可能落入陌生人手中,那么您需要在所有帐户上手动调用此备份;
- 我总是随身携带一部电话和一个 U2F令牌,因此,这种备份方法也不是理想的选择:一次丢失两者的可能性要比单独存储备份要高得多。 但是,可以通过使用例如Authy(将加密后的备份存储在其服务器上)的Authy对其进行少量补偿。
- 非通用方法:不幸的是,有足够多的服务仅提供自定义应用程序,并且不支持标准TOTP。
恢复代码:
- 恢复码必须存放在安全的地方。 同样,这个“安全的地方”很可能是我的家,与单独的U2F令牌几乎存在相同的问题;
- 同样,这是一种非通用的方法:每个服务都有自己的备份方法
因此,总而言之,所有这些方法都是非通用,繁琐且不太安全的。
最好的备份方法
现在,在我充分批评当前的状况之后,我终于要说出我真正想要的。 我真的想拥有两个U2F令牌:主令牌和备份令牌,但是必须以某种方式进行配置:
- 当我在任何设备上注册主令牌时,备用令牌将自动变为可用于该服务的状态;
- 在任何服务上使用备份令牌后,主令牌对该服务无效。
在讨论U2F中此技术的技术可行性之前,我将解释为什么它很棒以及如何使用它。
为什么很棒
如果我们看一下对上述独立备份令牌的批评,我们可以看到该方法的所有缺点都被消除了:
- 备份令牌应该不再容易访问。 极端的例子可能是:在砖墙上用砖头砌成令牌,或者在花园或其他地方埋一米半。 别开玩笑了,我已经准备好了。
- 无论我身在何处,如果我注册任何服务,都无需做任何事情即可向该服务添加备份令牌。 我只使用我的主令牌,并且知道自己有备份,因此我很放心。
- 对于局外人来说,完全不清楚我的备份令牌位于何处。 即使知道它的存在,试图自己找到它也几乎没有道理。
- 它足够安全。 即使我的主令牌发生了问题,同一事件也不太可能影响备份令牌。
- 它是普遍的。 此备份方法将在支持U2F的任何服务上起作用,无论该服务还支持什么。
如果主令牌确实发生了一些问题,那么我将执行以下操作:
- 我挖出/不清楚备份令牌;
- 使用U2F对我的所有服务进行身份验证,从而取消主令牌;
- 我订购了一对新的令牌,并在收到时在所有服务上添加了一个新的主令牌,并撤销了旧的令牌。
至少对于我个人而言,此策略是对高级别安全性和轻松备份负担的巨大折衷。 它比其他任何方法都更安全,更可靠。
实作
U2F协议概述
在讨论实现之前,我们需要在一定程度上了解U2F的工作方式。 大多数制造商都按以下方式实现它(并非标准中包含以下所有内容;有些东西是实现细节,但据我所知,大多数现有实现都是以这种方式工作的):
device_secret
与U2F令牌以及只能递增的32位
counter
一起编程。 当我们在服务上注册U2F令牌时,会发生以下情况:
- 浏览器将
AppID
(实际上是域名)发送到U2F设备。 - 设备生成一个随机数(
nonce
),将其与AppID
组合在一起,并使用device_secret
作为密钥将其全部通过HMAC-SHA256传递,并且产生的哈希成为该特定服务的私钥: service_private_key
; - 从
service_private_key
生成公钥service_public_key
; - 设备再次获取
AppID
,将其与service_private_key
合并,然后使用device_secret
作为密钥再次将其通过HMAC-SHA256。 结果( MAC
)以及之前生成的nonce
成为key_handle
; - 设备将
key_handle
和service_public_key
发送回浏览器,然后浏览器传递给服务,该服务会保存此数据以供将来进行身份验证。
后续身份验证的过程如下:
- 该服务生成一个
challenge
(随机生成的数据),并将其与key_handle
(由nonce
和MAC
)一起发送到浏览器。 浏览器将所有这些信息以及AppID
(即域名)传递给设备; - 具有
nonce
和AppID
的设备将生成service_private_key
,其方式与注册期间生成的方式相同。 - 设备以与注册期间相同的方式生成
MAC
,并将其与从浏览器接收到的MAC
进行比较,从而确保不替换nonce
,因此service_private_key
可靠的。 - 设备递增
counter
; - 设备使用
service_private_key
对challenge
, AppID
和counter
进行签名,并将生成的签名(sign)和counter
浏览器,浏览器将该数据进一步传输到服务; - 服务使用注册后使用的
service_public_key
检查signature
。 同样,大多数服务会验证counter
大于先前的值(如果这不是第一次身份验证)。 此测试的目的是使无法访问U2F设备。 结果,如果signature
匹配并且counter
大于先前的值,则认为认证已成功完成,并且该服务将保存新的counter
值。
现在,让我们概述与讨论直接相关的细节。
利益详情
首先是设备不会为每个服务存储
service_private_key
:而是每次使用HMAC-SHA256都显示
service_private_key
。 这对我们非常重要:很明显,如果每个设备将为每个服务分别存储唯一密钥,那么只有该设备可以随后进行身份验证。
顺便说一下,这不是U2F的要求:U2F并不指示密钥的存储方式,实际上,U2F的某些早期实现确实为每个服务分别存储了密钥。 这种方法的缺点是可以使用该设备的服务数量受到限制。 service_private_key
的派生消除了此缺点。其次,该设备具有防止克隆的
counter
。
乍一看,该
counter
似乎不允许我们实施所讨论的备份策略(至少在我试图找到解决方案时,对我来说似乎如此),但实际上,它只能帮助我们! 我现在解释。
主要思想
这个想法是这样的:在生产阶段,以两个令牌都具有相同
device_secret
的方式编程两个令牌,但是备份令牌需要进行一些纠正:与其使用纯形式的
counter
(如普通令牌那样),还应添加一些大常数要
counter
。 例如,32位范围的一半,即 大约
2 000 000 000
,这看起来很合理:我一生都不会用尽这么多认证。
实际上,仅此而已。 简单有效。
以这种方式对两个令牌进行编程后,我将备份令牌隐藏在一个
真正难以到达的地方,从不碰它。 如果发生了可怕的事情并且我无法访问主令牌,那么我仍然可以访问备份令牌,并且可以立即在注册主令牌的所有服务上使用它,因为 备份具有相同的
device_secret
,其
counter
以一个非常大的数字开头,这在我的余生中都不会得到。
另外,我提请注意以下事实:
我不建议克隆令牌 。 两个令牌尽管具有相同的
device_secret
,但具有不同的计数器,并且在对
device_secret
进行编程之后,应该没有办法从设备取回令牌或以任何其他方式创建克隆。
关于计数器的注意事项
细心的读者可能会注意到存在以下安全问题:如果攻击者获得对主令牌的访问权并以某种方式发起2,000,000,000身份验证,该怎么办? 然后,即使在该服务上使用了备份令牌之后,他也可以访问该服务。
幸运的是,这个问题有一个简单的解决方案。 在任何情况下,计数器都必须在硬件中实现(大概在某种加密处理器上),并且为了安全实现,此硬件计数器的范围必须小于32位。 例如,在
ATECC508A上,计数器最多只能计数2097151,因此通过将添加到计数器的常量设置为大于计数器最大值的任何值,我们可以确保主令牌永远不会计入备用令牌中的计数器。
需要说明的是:假设我们的U2F令牌使用ATECC508A,并将ATECC508A内部的计数器表示为
hw_counter
。 然后:
- 在主令牌中,我们用于计算:
hw_counter
; - 在备份令牌中,我们用于计算:
hw_counter + 2000000000
。
请注意,我们不会在加密处理器内部修改真实的
hw_counter
; 它仍然会从0到2097151进行计数。相反,每次需要获取计数器值时,我们
hw_counter
从ATECC508A中读取hw_counter,然后添加常量并将其返回(以用于U2F的进一步计算)。
因此,主令牌中计数器值的范围将为[0,2097151],而备用令牌中计数器值的范围将为[2000000000,2002097151]。 这些范围不重叠的事实确保了在使用备份时取消主令牌(如果服务使用
counter
;我检查的主服务使用了它)。
实际执行
我所了解的U2F令牌制造商中没有一家现在支持所需的自定义。 但幸运的是,有一个U2F令牌的开源实现:
SoloKeys 。
一年前,我写了我的原始文章(英文),这部分过时了:那时SoloKeys处于原型开发阶段,并且使用了项目的先前迭代:
u2f-zero 。 因此,我现在不翻译这部分内容,因为获得u2f-zero设备的唯一方法是自己焊接,并且不建议这样做(尽管github上有说明)。
尽管如此,
原始文章中给出了u2f-zero必要修改的所有详细信息。
当我的手接触到独奏时,我将写出修改说明。
无论如何,这是我今天知道的唯一获得可靠备份的可用U2F令牌的方法。 检查几个服务(至少是google和github)表明它有效:通过在服务上注册主要令牌,我们还可以使用备份,并且在首次使用备份后,主要令牌停止工作。 Awwwwwww。 <3
警告
尽管这种备份策略很酷,但我不确定通过u2f-zero或solokey具体实现什么。 这条路是获得您想要的东西的唯一途径,所以我走了那条路。 但是假设攻击者已经获得了对U2F设备的物理访问权限,我不确定黑客攻击该设备(即从该设备获取
device_secret
)是否会像Yubikey或其他主要制造商一样困难。 solokey的作者声称“安全级别与现代汽车钥匙相同”,但是我没有进行任何检查来确认这一点。
但是,老实说,我并不为此担心。 如果攻击者只是简单地窃取令牌而无意将其返回,那么破解它的复杂性就没有关系,因为 攻击者可以简单地使用此令牌访问帐户,例如,简单地撤销此令牌并添加另一个。 但是,为此,我还必须面临其他严重的安全问题。 U2F令牌只是第二个因素。
因此,唯一的安全性可能比其他方式低的情况是,攻击者尝试在短时间内访问设备,从中获取
device_secret
并将设备退回给我,这是无形的。 为此,他需要读取闪存微控制器(或在适当的时间RAM)的内容,这不是很简单。
考虑到所有因素,我认为对我个人而言,拥有可靠的备份比拥有U2F设备的超安全硬件实现更为重要。 这样安全的实施和缺少良好备份的问题的可能性高于u2f-zero(solokey)和备份的问题的可能性。
结论
所考虑的备份策略在所有方面都胜过其他选择:它比其他任何方法都通用,安全和可靠。
如果至少有一家主要制造商在其产品中实现此功能,我将感到高兴,但目前还不确定。 Yubico的一名支持人员James A.甚至告诉我,备份是我所需要的,“用U2F的设计方式是不可能的”,在我列出了实现细节之后,它就停止了响应。
幸运的是,这并非像尤比科所认为的那样不可能。
我的英文原著: U2F令牌的可靠,安全和通用备份 。 因为 原始文章的作者是我本人,因此,在您允许的情况下,我并未将本文归类为“翻译” 。