「某某大型服务的密码又泄露了」这样的新闻,如今已经没有人会感到惊讶。即便每年都开展钓鱼演练,上钩的人也从未归零。「密码不要重复使用、要设置得更长、定期更换……现在已经不用了」──说法也是几经反复。
近几年迅速普及的通行密钥(passkey),正是 Apple、Google、微软三家公司共同推动、作为这种局面之答案的认证方式。1 很多介绍都停留在「用指纹或人脸就能登录,很方便」这一层面,但本质并不在此。通行密钥真正的价值,在于把安全性的立足点从「人的注意力」转移到了「协议的结构」上。
- 密码之所以泄露是因为用户不小心,所以要加强教育 → 人终究会犯错
- 训练用户识破假网站 → 总能做出让人识别不出的假网站
- 而通行密钥则是 → 压根就没有需要发送的秘密,在假网站上签名根本无法成立
本文将从「密码究竟坏在哪里」出发,用图解来把握通行密钥为什么安全。在此基础上,正面回答「会同步的通行密钥真的安全吗」「难道没有弱点吗」这些理所当然的疑问,最后整理在 Web 应用或 Windows 环境中导入时的实务要点。
1. 先说结论
通行密钥之所以安全,可以归纳为以下三点。
- 服务器上不存在秘密。服务器保存的只有公钥,这是即使泄露也无法被恶意利用的信息。就算数据库整体外泄,攻击者也带不走能够用来冒充的材料。2
- 秘密不会在网络上传输。登录时发送的,只是针对当场生成的随机数(挑战值)所作的签名。私钥完全不会从设备的认证器中流出,因此无论在传输路径的哪个环节窃听或中继,都无法获取秘密。2
- 在假网站上签名无法成立。通行密钥与网站的域名绑定,浏览器会强制核对域名。即使用户被假网站骗过,真正网站专用的通行密钥本就不会出现在候选之中;就算中继了签名,也会在验证环节被拒绝。3
这三点并非各自独立的巧思,而是全部源自同一个设计转变——从「共享并发送秘密」的认证,转向「用签名证明自己持有秘密」的认证——所导出的结果。下面依次来看。
术语之间的关系 ── 通行密钥・WebAuthn・FIDO2・CTAP
这个领域术语繁多,而且不同文章所指的范围也不尽相同,所以先把它们之间的关系理清楚。通行密钥并非一种新协议,而是给既有规格的组合起的一个「称呼」。21
| 术语 | 正式名称 | 指的是什么 |
|---|---|---|
| WebAuthn | Web Authentication API(W3C 推荐标准) | 浏览器与网站之间的规范。通过 navigator.credentials 请求创建密钥对及签名的 API |
| CTAP | Client to Authenticator Protocol(FIDO 联盟) | 浏览器与外接认证器之间的规范。经由 USB・NFC・蓝牙与安全密钥或手机通信的部分 |
| FIDO2 | — | 上述两者合并而成的框架总称。FIDO2 = WebAuthn + CTAP |
| 通行密钥(passkey) | — | 在 FIDO2 凭据中,能够替代密码单独完成登录的一类(可发现凭据)的称呼 |
表1:通行密钥是建立在 FIDO2 这一基础之上的称呼,并不是规格名称
也就是说,所谓「支持通行密钥」,换成实现层面的说法就是「实现 WebAuthn」。CTAP 是在使用外接认证器时由浏览器和操作系统负责处理的一层,开发 Web 应用的一方不会直接接触到它。
2. 密码认证究竟坏在哪里
理解通行密钥安全性的捷径,是按「位置」来把握密码的弱点。在密码认证中,秘密本身在每次认证时都会走遍全部区间。
sequenceDiagram
participant U as 用户
participant B as 浏览器
participant S as 服务器
Note over U: 在脑海中保存秘密(密码)<br/>【弱点①】可被猜测・被重复使用
U->>B: 输入密码
Note over B: 【弱点②】在假网站上同样能够<br/>输入(仅凭外观无法区分)
B->>S: 直接发送密码本身
Note over B,S: 【弱点③】秘密在传输路径上流动<br/>虽有 TLS 保护,但在终端会还原为明文
S->>S: 与已保存的哈希值进行比对
Note over S: 【弱点④】所有用户的秘密(哈希值)都汇集于此<br/>一旦泄露就会成为离线暴力破解的目标
图1:在密码认证中,秘密本身存在于所有区间
从攻击者的角度看,这是一种靶子多、容易命中的结构。
- 弱点①(用户):强度仅限于人能记住的程度,并且会在多个网站上重复使用。一处泄露就会波及所有账户(撞库攻击)。
- 弱点②(输入的瞬间):只要准备一个与真网站难以区分的假网站,用户就会主动把秘密交出来(钓鱼攻击)。
- 弱点③(传输路径):由于有 TLS,直接窃听传输路径本身很困难,但一旦被插入一个「外观正规的中继点」,防护就形同虚设(即后文所述的 AiTM)。
- 弱点④(服务器):即便以哈希形式保存,一旦数据库外泄,也能被离线暴力破解。强度较弱的密码会依次被破解。
「那么加上一次性验证码(短信或 TOTP)不就行了吗」——这就是传统的多因素认证,但它同样没有摆脱发送共享秘密的结构。TOTP 是服务器与认证 App 共享同一个种子(秘密),而生成出来的 6 位数验证码,用户终归还是能够输入到假网站里去。实际上,假网站将请求实时中继给真服务器的AiTM(Adversary-in-the-Middle,中间人)型钓鱼攻击,正是把「密码+一次性验证码」这一整套原样转发出去从而得手的。CISA(美国网络安全与基础设施安全局)将「具有抗钓鱼能力的 MFA」仅列举为 FIDO/WebAuthn 方式,以及智能卡(PIV/CAC)等基于 PKI 的认证这两种,并且把 FIDO 定位为其中的黄金标准,原因正在于此。4
也就是说,问题并不在于密码的「强度」,而在于「共享秘密、并在每次认证时都发送」这一结构本身。
3. 通行密钥的真面目 ── 不发送秘密,只证明自己持有秘密
通行密钥是建立在 W3C 的WebAuthn与 FIDO 联盟的CTAP这两项标准(合称 FIDO2)之上、基于公钥加密的凭据。21 听起来似乎很复杂,但结构其实很单纯。
flowchart LR
subgraph DEV["用户的设备"]
AUTH["认证器——保险柜<br/>Windows Hello / Face ID /<br/>Android 屏幕锁 / 安全密钥"]
SK["私钥<br/>绝不会从这里流出"]
BIO["指纹・人脸・PIN<br/>= 仅用于打开保险柜的门<br/>同样不会流出"]
AUTH --- SK
BIO -->|"在本地进行核对"| AUTH
end
subgraph SRV["服务器"]
PK["公钥<br/>即使泄露也无法被恶意利用<br/>「仅用于验证」的信息"]
end
SK -.->|"数学上成对<br/>-制作签名的一方"| PK
图2:通行密钥的实体是按网站生成的密钥对。私钥一侧不会离开设备,服务器只持有用于验证的公钥
- 私钥是能够生成签名的那一侧的密钥,保存在设备内的认证器(Windows Hello、iPhone 的 Face ID/Touch ID、Android 的屏幕锁,或是 YubiKey 之类的安全密钥)中,不会流出。
- 公钥是只能用来验证签名的那一侧的密钥,会被交给服务器保管。从公钥反推出私钥在计算量上是不可能的,因此这是即使泄露也无妨的信息。
- 指纹、人脸等生物特征信息,仅用于在本地打开保险柜的门,同样不会离开设备。生物特征信息不会被发送到服务器。1
注册:只交出公钥
这是在网站上注册通行密钥时的流程。
sequenceDiagram
participant S as 服务器(example.com)
participant B as 浏览器
participant A as 认证器
S->>B: 注册请求(随机挑战值 + 网站信息)
B->>A: 请为这个网站(example.com)生成密钥
A->>A: 通过指纹・面容・PIN 进行本人验证(本地)
A->>A: 生成新的密钥对<br/>私钥保存在内部
A->>B: 公钥 + credential ID(密钥的标签)
B->>S: 发送公钥 + credential ID
S->>S: 作为该账户的公钥保存
Note over S: 服务器收到的<br/>只是「即使泄露也无法被恶意利用的信息」
图3:注册时在网络上传输、并保存在服务器上的只有公钥
重要的是,此时生成的密钥对会与网站的域名(RP ID)绑定在一起。为 example.com 生成的通行密钥,只能在 example.com 这个网站上使用(由于 RP ID 是以域名为单位的,因此可以从 login.example.com 这类同一域名下的子域名页面使用,但无法从毫不相关的域名使用)。这种绑定关系,正是后文所述抗钓鱼能力的基础。3
此外,密钥对每次都会针对每个网站重新生成。网站 A 与网站 B 的通行密钥在数学上互不相关,因此「重复使用」这个概念本身根本不存在,也无法被用作在不同网站之间比对用户身份的材料。
认证:返回一次性的签名
这是登录时的流程,请与密码认证(图1)对照来看。
sequenceDiagram
participant S as 服务器(example.com)
participant B as 浏览器
participant A as 认证器
S->>B: 登录请求(一次性随机挑战值)
B->>A: 请求为 example.com 生成签名
A->>A: 通过指纹・面容・PIN 进行本人验证(本地)
A->>A: 用私钥生成签名<br/>将挑战值 + 来源(origin) + RP ID 哈希写入签名
A->>B: 签名(并非私钥本身)
B->>S: 发送签名
S->>S: 用保存的公钥验证签名<br/>同时核对挑战值・来源・RP ID
Note over B,S: 在传输路径上流动的只是一次性的签名<br/>即使被窃取,也无法用于下一次的挑战
图4:认证时秘密同样不会移动。流动的只是「当场使用一次的证明文件」
服务器每次都会出一道新的随机数(挑战值)题目,认证器则针对「该挑战值 + 浏览器当前所在的来源(origin) + RP ID 的哈希」生成签名。服务器用保存的公钥验证签名,确认挑战值确实是自己出的题目,来源与 RP ID 也确实属于自己的网站。5
作为这种设计的必然结果,开头所说三个理由中的两个已经成立。
- 服务器上没有秘密:保存的只有公钥。即使泄露,攻击者也无法生成签名,不像密码的哈希那样可以「带回去破解」。
- 秘密不会流动:即使窃取了传输路径上的签名,由于挑战值是一次性的,也无法重复利用(重放)。
剩下的一点——「在假网站上签名无法成立」——正是通行密钥最大的卖点。下面另立一节详细说明。
4. 钓鱼攻击为何在「结构上」无法成立
针对密码的钓鱼攻击之所以能够得逞,是因为能够把真正的秘密输入到假网站里。人在(尤其是疲惫的时候)很难区分 example.com 与 examp1e.com,而密码输入框在这两个网站上的表现却毫无二致。
而在通行密钥中,这项核对不是由人来完成,而是由浏览器机械地执行。按照 WebAuthn 的规范,只有在「当前显示页面来源的域名」与「通行密钥的 RP ID」相对应时,浏览器才能够调用认证器。3 下面用图来说明访问假网站的那一刻会发生什么。
sequenceDiagram
participant U as 用户
participant B as 浏览器
participant P as 假网站(examp1e.com)<br/>向真网站转发的 AiTM 代理
participant S as 真正的服务器(example.com)
U->>P: 访问外观极其相似的登录页面
P->>S: (在背后)启动真正的登录流程
S->>P: 挑战值
P->>B: 转发挑战值并请求签名
B->>B: 当前来源是 examp1e.com<br/>无法把 example.com 专用的通行密钥列为候选
B--xP: 不会生成签名(用户根本无从被骗)
Note over B,S: 即便通过某种方式生成了签名,<br/>由于签名中已写入 examp1e.com,<br/>在真正服务器的验证环节也必定会被拒绝
图5:AiTM 型钓鱼攻击能够突破「密码+一次性验证码」,但对通行密钥而言,在签名阶段就无法成立
请注意,这里的防御是双重的。
- 不会出现在候选中:浏览器只会列出与当前来源相对应的 RP ID 所属的通行密钥。在假域名上,真网站专用的通行密钥根本不会作为选项出现,用户连「不小心用了」都做不到。
- 签名无法通过:签名的对象中包含了浏览器确认过的来源与 RP ID 的哈希。真正的服务器在验证时会核对这些信息,因此在其他来源生成的签名必定会被拒绝。5
针对密码的钓鱼防御,一直依赖于「用户仔细查看 URL」这种人的努力。而在通行密钥中,用户压根就不需要识破假网站。这正是「抗钓鱼(phishing-resistant)」一词的准确含义,也是 CISA 与 NIST(美国国家标准与技术研究院)对 FIDO/WebAuthn 方式给予特殊待遇的原因。46
下面按攻击手法,把到目前为止的内容整理成表格。
| 攻击 | 密码 | 密码+TOTP | 通行密钥 |
|---|---|---|---|
| 猜测・暴力破解 | ✗ 脆弱 | △ 验证码能挡住,但原始密码依然脆弱 | ○ 不存在可供猜测的对象 |
| 重复使用(撞库攻击) | ✗ 一处泄露波及全部 | △ 从未启用验证码的网站开始被攻破 | ○ 每个网站的密钥相互独立 |
| 服务器数据库泄露 | ✗ 哈希值可离线暴力破解 | ✗ TOTP 的种子(共享秘密)也会一并泄露 | ○ 只有公钥 |
| 传统钓鱼攻击(在假网站上诱导输入) | ✗ 会被输入进去 | ✗ 验证码也会被输入进去 | ○ 不会出现在候选中,签名也无法通过 |
| AiTM(实时中继) | ✗ 原样被转发出去 | ✗ 连同验证码一起被转发出去 | ○ 因来源核对而无法生成有效签名 |
| 重放(通信的重复利用) | ✗ 同一密码可反复多次生效 | △ 在本人使用之前被截获的验证码依然有效(若实现正确,已使用过的验证码会被拒绝再次接受) | ○ 挑战值每次都是一次性的 |
表2:按攻击手法划分的抗性对比。通行密钥一栏中的「○」无一例外都源于结构本身,而非依赖运维或注意力
5. 「会同步的通行密钥」安全吗
读到这里,自然会产生这样的疑问:「不是说私钥不会离开设备吗,那为什么在 iPhone 上创建的通行密钥在 iPad 上也能用?」──这是个好问题,答案是「通行密钥有两种」。
先把结论做成速查表放在前面。请把接下来这一节和下一节,当作是在解释表中每一行为什么会是这样来阅读。
| 视角 | 同步型通行密钥 | 设备绑定型通行密钥 |
|---|---|---|
| 代表示例 | iCloud 钥匙串、Google 密码管理器、1Password 等密码管理器 | 安全密钥(YubiKey 等)、Windows Hello、Microsoft Authenticator 内的通行密钥 |
| 私钥的保管位置 | 平台的凭据保险库。以端到端加密的形式在同一账户的各设备之间复制 | 认证器的硬件内部。不会流出 TPM 或安全元件之外 |
| 丢失・更换设备时 | 只要登录同一个 Apple ID/Google 账户,即可在新设备上恢复 | 该认证器上的通行密钥会丢失。前提是预先注册多个备用认证器 |
| 单点故障 | 平台的云账户 | 物理设备本身 |
| 是否适合企业管理 | 由于密钥进入个人的云账户,组织一方难以掌握其所在或统一吊销。适合 BYOD 或小规模场景 | 管理员可以分发・吊销,密钥所在位置明确。适合规程严格的环境 |
| 对 NIST AAL 的符合情况 | 满足要求即可达到 AAL2。由于私钥可导出,无法用于 AAL36 | 由硬件保护、密钥无法取出的认证器,也可能满足 AAL3 所要求的条件6 |
表3:同步型与设备绑定型速查表。选择哪一种,取决于更看重「抗丢失能力」还是「可管理密钥所在位置」
flowchart TB
subgraph SYNC["同步型通行密钥-面向消费者的默认选项"]
S1["iCloud 钥匙串 /<br/>Google 密码管理器 /<br/>1Password 等密码管理器"]
S2["在同一账户的设备之间<br/>以端到端加密方式同步<br/>服务商也无法读取内容"]
S3["优点-更换设备・丢失时依然可靠<br/>注意-需要加强云账户自身的防护"]
S1 --> S2 --> S3
end
subgraph BOUND["设备绑定型通行密钥"]
B1["安全密钥-如 YubiKey 等 /<br/>Windows Hello /<br/>Microsoft Authenticator-Entra ID"]
B2["私钥不会从该硬件中<br/>物理性地流出-由 TPM 等保护"]
B3["优点-密钥所在位置单一明确<br/>注意-为应对丢失必须注册多个"]
B1 --> B2 --> B3
end
SYNC ~~~ BOUND
图6:同步型与设备绑定型。两者在「私钥不发送给服务器」这一点上相同,只是防护的重心不同
同步型通行密钥,是指 iCloud 钥匙串或 Google 密码管理器把私钥在同一账户的各设备之间进行同步的那种类型。这里重要的是,同步过程采用了端到端加密。Apple 与 Google 都明确表示,通行密钥会先在设备上加密再进行同步,就连服务商自己也无法读取其中的内容。78 也就是说,「私钥不会离开设备」这条原则被更精确地放宽为「私钥不会以明文形式离开设备」,作为交换,得到了对更换设备或丢失的抗性。
这种放宽会如何改变威胁模型,有必要明确说清楚。需要防护的对象,从「每个网站的服务器」集中到了「单一的云账户」。对各网站的数据库泄露和钓鱼攻击依然保持很强的抵抗力,而这一次,Apple ID/Google 账户本身被劫持则成为了新的单点故障。正因如此,对存放通行密钥的平台账户施加最强的保护(强密码锁屏、梳理好找回手段、条件允许的话再配上物理安全密钥)是一个大前提。NIST 也在 2024 年 4 月发布了NIST SP 800-63B 的补充文件(Supplement 1: Incorporating Syncable Authenticators Into NIST SP 800-63B),正式把这种同步型通行密钥(syncable authenticator)在满足条件的情况下,定位为可以达到政府标准中的AAL2(Authenticator Assurance Level 2,认证器保证级别 2)。不过,由于私钥存在被导出的可能性,要求硬件隔离环境的 AAL3 规定不得使用同步型认证器。6
设备绑定型通行密钥,是私钥不会离开硬件的类型。以 YubiKey 之类的安全密钥为典型代表,面向企业场景时,Microsoft Entra ID 的通行密钥(在 Microsoft Authenticator 内创建的那种)同样属于设备绑定型。9 Windows 的 Windows Hello 也是一样,只要有 TPM,就会把私钥保护在 TPM 之下。这种「不让密钥流出硬件保险柜」的机制本身,与TPM 一文中详细讲解过的是同一套基础。
另外,或许你曾对「用手机的通行密钥登录 PC 浏览器」时被要求扫描二维码这件事感到不解。那并不只是单纯的画面跳转,而是一种通过蓝牙确认手机与 PC 物理上处于同一位置的混合方式(FIDO 的跨设备认证)。它借助这种近距离确认,堵死了远程攻击者试图通过二维码诱使他人替自己批准登录 PC 的攻击手法。1
6. 并非银弹 ── 弱点不是「消失」,而是「转移」
到这里为止,一直在说明通行密钥的强大之处,但坦诚地说,通行密钥并不能消灭攻击,而是一种把攻击者赶往更薄弱之处的技术。当认证这道大门变得坚固之后,攻击者会转向何处,下面用图来说明。
flowchart LR
A["攻击者"]
G["认证本身<br/>挑战值签名<br/>【坚固】"]
R["账户找回流程<br/>谎称『通行密钥丢失了』<br/>诱导通过短信或邮件重新设置<br/>进而注册攻击者自己的通行密钥"]
F["并存的后备手段<br/>如果密码・短信登录<br/>依然保留,那里就是最薄弱的环节"]
C["云账户<br/>若为同步型,Apple ID /<br/>Google 账户就是单点故障"]
SS["会话<br/>只要窃取登录后的 Cookie<br/>与认证方式无关"]
A --x G
A --> R
A --> F
A --> C
A --> SS
图7:当大门(认证)变得坚固后,攻击会转向找回流程・并存手段・云账户・会话
实务中需要掌握的残余风险有四个。
- 并存的后备手段会成为最薄弱的环节。如果只是让通行密钥「也」能使用,而密码或短信登录依然保留,攻击者只需转而使用那些手段即可。从账户整体来看,抗钓鱼能力会被最弱的那种登录方式的水平所限制。导入的重点并不在于新增通行密钥,而在于有计划地缩减・废止后备手段。
- 找回流程会成为新的攻击面。这是一种谎称「设备丢了」,从而通过支持窗口或邮件走上重新设置的流程,进而注册攻击者自己的通行密钥的手法。实际上,绕过坚固的认证、去欺骗帮助台的社会工程学手段,已经成为大规模入侵事件的惯用套路。认证被加固了多少,找回流程中本人验证该如何设计,就会被追问多少。
- 在同步型中,云账户是单点故障。正如前一节所述。既需要加强存放通行密钥的账户的防护,在组织中使用时也需要就「允许同步到哪个平台」制定方针。
- 无法防止会话窃取。通行密钥所保护的只是登录那一瞬间,一旦登录后的会话 Cookie 被恶意软件或 XSS 窃取,与所用的认证方式便毫无关系。令牌的有效期限管理、绑定、设备健全性管理,这些属于另一层面的工作依然存在。
以上并不是说「还是别用通行密钥了」,而是一个理所当然的道理——即便把大门的锁换成最新款,窗户要不要上锁是另一回事。反而正因为弱点所在变得明确,才更能把防御资源集中投入到找回流程与会话管理上。
7. 实务导入 ── WebAuthn API 与 Windows 环境
最后,从导入方的视角来把握要点。
为自家 Web 服务加入通行密钥登录
浏览器一侧只需用到 WebAuthn API 的两个函数:注册调用 navigator.credentials.create(),认证调用 navigator.credentials.get()。
// 注册(浏览器端)
const credential = await navigator.credentials.create({
publicKey: {
challenge: challengeFromServer, // 服务器生成的一次性随机数
rp: { id: "example.com", name: "Example" },
user: { id: userIdBytes, name: "taro@example.com", displayName: "太郎" },
pubKeyCredParams: [{ type: "public-key", alg: -7 }], // ES256
authenticatorSelection: {
residentKey: "required", // 设为可发现凭据(=通行密钥)
userVerification: "required", // 强制要求通过生物识别/PIN进行本人验证
},
},
});
// 公钥从 credential.response 中获取,credential ID 则从顶层的
// credential.id / credential.rawId 中获取。注册响应也应像认证时
// 一样,先在服务器端验证挑战值・来源・RP ID,然后再保存
主要参数的含义如下。
| 参数 | 作用 | 实现时的注意事项 |
|---|---|---|
challenge |
服务器每次生成的一次性随机数 | 使用无法被推测的密码学随机数。服务器端需验证「确实是自己签发且尚未使用过的」之后再消费 |
rp.id |
凭据所绑定的域名(RP ID) | 省略时会取调用方来源(origin)的有效域名。只能在可注册域名的范围内指定,例如从 login.example.com 指定为 example.com |
user.id |
服务器内部的用户标识符(user handle) | 64 字节以下的不透明值。不要直接放入邮箱地址或用户名等可识别个人的信息2 |
user.name / user.displayName |
显示在认证器或浏览器界面上、供用户选择账户的字符串 | 仅用于显示。服务器不能凭此值来信任并确定账户 |
pubKeyCredParams |
按优先顺序列出可接受的公钥算法 | 除 -7(ES256)外,一并列出 -257(RS256)可以扩大能够接受的认证器范围 |
authenticatorSelection |
对认证器提出的要求 | 设为 residentKey: "required" 即成为通行密钥(可发现凭据),设为 userVerification: "required" 则强制要求通过生物识别・PIN 进行本人验证 |
表4:navigator.credentials.create() 的主要参数
验证环境注意事项:由于 WebAuthn API 只在安全上下文中才会被公开,因此以裸 http:// 提供的页面中,调用 navigator.credentials 会失败。例外是 http://localhost(以及 127.0.0.1),它们会被当作可信来源处理,因此在开发机上即便不做 HTTPS 化也可以直接试用。不过 RP ID 必须是来源的有效域名(或其父域名),所以在 localhost 上创建的通行密钥无法在生产域名上使用。即便手边没有认证器,只要在 Chrome 开发者工具的「WebAuthn」标签页中启用虚拟认证器,也能把从注册到认证的整个流程跑一遍。
// 认证(浏览器端)
const assertion = await navigator.credentials.get({
publicKey: {
challenge: challengeFromServer,
rpId: "example.com",
userVerification: "required",
},
// 将通行密钥作为登录框的自动填充候选项呈现。请预先通过
// PublicKeyCredential.isConditionalMediationAvailable() 确认是否支持,
// 在不支持的浏览器中回退为不带 mediation 参数的普通调用
mediation: "conditional",
// (需要在目标 <input> 上指定 autocomplete="username webauthn")
});
// 在服务器端验证 assertion.response 中的签名
核心工作在于服务器端的验证。以下几项是必须做到的最低限度。5
- 核对挑战值:是否是自己签发的、尚未使用过的挑战值;是否做到了一次性使用(防重放)。此外,还要在签发时与浏览器会话(登录尝试)绑定保存,只允许来自同一会话的响应消费它。如果这里做得不够严格,就会给攻击者留下可乘之机:让受害者的浏览器帮忙送出针对攻击者自己那份挑战值的签名,从而使受害者登录到攻击者的账户中(登录 CSRF)。
- 核对来源:
clientDataJSON中的origin是否是自家网站的正规来源(这是抗钓鱼的关键)。 - 核对 RP ID 哈希:
authenticatorData中的rpIdHash是否与自家网站 RP ID 的 SHA-256 一致。 - 确认操作类型与标志位:
clientDataJSON中的type,在认证时是否为webauthn.get,注册时是否为webauthn.create;authenticatorData中的 UP(用户在场)标志是否已置位;如果要求了用户验证(UV),UV 标志是否也已置位。 - 验证签名:能否用注册时保存的公钥正确验证签名。
- 与账户的绑定:用提交上来的 credential ID(以及 userHandle)在自己的数据库中查询,并向该凭据的所有者签发会话。如果无条件信任另外输入的用户名,就会出现一个漏洞——用正确的签名却把用户登录成了别人。
- 保存并比较签名计数器:按凭据分别保存
authenticatorData中的 signCount,并确认下一次的值比上一次更大。若小于或等于上一次的值(含相同的情况),则应视为认证器被复制(克隆)的迹象。不过由于许多实现中同步型通行密钥总是返回 0,因此仅当两次都是 0 时才作为例外予以放行。
自己手写这套验证逻辑很容易埋下事故隐患,请使用经过实践检验的库(.NET 用 fido2-net-lib,Node.js 用 SimpleWebAuthn 等)。把规范中的细节(CBOR 解析、算法协商、挑战值管理)交给库去处理,自己则把精力集中在挑战值的保存与失效、多个通行密钥的管理界面、找回流程的设计上,这才是正确的力量分配方式。
Windows 环境・企业内部系统的情况
对于本站的主要读者──「负责 Windows 业务系统」的这一立场而言,只需掌握以下三点就足够了。
- Windows 客户端已经支持。Windows 11 支持以 Windows Hello 作为认证器来创建・使用・管理通行密钥(设置 > 账户 > 通行密钥),只要有 TPM,私钥就会得到硬件保护。10 通过浏览器(Edge/Chrome)使用的 WebAuthn,在 Windows 10 上也能运行。
- 在 Entra ID 环境中启用「通行密钥=FIDO2 认证方式」。Microsoft Entra ID 支持安全密钥以及 Microsoft Authenticator App 内的通行密钥(设备绑定型),只要在条件访问的认证强度中要求「抗钓鱼 MFA」,就能把对目标资源的访问限定为通行密钥等方式。9 从 NTLM 与密码有效期策略的世界迁移过来不可能一蹴而就,因此常见的做法是,在与NTLM 与 Kerberos 一文中所讨论的认证基础设施盘点并行推进的同时,先从管理员账户开始强制启用抗钓鱼 MFA。
- 企业内部的 Web 应用同样能获得同样的收益,但前提是要有 HTTPS。由于 WebAuthn API 只能在安全上下文中运行,因此即便是内网应用(除开发时的 localhost 外),也必须优先完成 HTTPS 化,以及整理出可以用作 RP ID 的内部域名。只要做好这一步,RP ID 对内部域名同样有效。就「告别贴在显示器上的密码便签」这层意义而言,企业内部往往比对外服务更快见到效果,这种情况并不罕见。
8. 总结
- 密码的弱点不在于强度,而在于「共享秘密、并在每次认证时都发送」这一结构。由于秘密同时存在于用户、输入框、传输路径、服务器等所有环节,攻击面因此很多。即便加上一次性验证码,面对 AiTM 型钓鱼攻击,仍会因被中继而落败。
- 通行密钥是按网站各自生成的公钥加密密钥对,交给服务器的只是即使泄露也无法被恶意利用的公钥,登录时发送的也只是针对一次性挑战值的签名。服务器上没有秘密,传输路径上也不会流动秘密。
- 由于签名中写入了浏览器确认过的来源与 RP ID,假网站上不会出现真网站专用的通行密钥,即便被中继也会在验证环节被拒绝。用户无需识破假网站,这正是「抗钓鱼」的真面目,防御的立足点已经从人的注意力转移到了协议的结构上。
- 同步型通行密钥通过端到端加密进行同步,对更换设备和丢失有较强的抵抗力。作为交换,需要防护的对象集中到了云账户上,因此 Apple ID/Google 账户自身的防护便成为大前提。企业也可以选择设备绑定型(安全密钥、Entra ID 的 Authenticator 通行密钥)。
- 通行密钥并不能消除攻击,只是把攻击赶向更薄弱之处。并存的密码、账户找回流程、会话窃取,都是依然存在的攻击面,导入工作的重点在于有计划地缩减后备手段以及加强找回流程。
- 实现方式是 WebAuthn API 的
create/get这两个函数,加上服务器端验证。验证不要自己造轮子,交给经过实践检验的库,把精力分配在挑战值管理、多通行密钥的界面、找回设计上。
相关文章
- Windows的TPM是什么 ── 图解「不外泄密钥的保险柜」与度量启动
- 图解 NTLM 与 Kerberos ── 为什么认证会「回退」到 NTLM
- 在 WinForms/WPF 应用中集成 Entra ID 认证 —— MSAL.NET 与 WAM Broker 的实务架构
- PowerShell 中凭据的安全处理方式 ── 把明文密码逐出脚本
- Windows 应用程序开发安全性最低限度检查清单
- 中小企业的安全对策,该从何入手 ── IPA《中小企业信息安全对策指南》第4.0版导读
相关咨询领域
合同会社小村软件承接的委托开发业务,涵盖为企业内部 Web 系统实现通行密钥登录(WebAuthn)提供支持、设计 Entra ID 环境下的抗钓鱼 MFA 部署方案,以及将认证功能嵌入 WinForms/WPF 等 Windows 业务应用等内容。
-
FIDO 联盟,Passkeys(通行密钥)以及How FIDO Works。关于通行密钥作为 FIDO 凭据用于取代密码,生物特征信息不会从设备发送出去、只用于本地核对,2022 年 5 月 Apple・Google・微软共同宣布将扩大对基于 FIDO 标准的无密码方式的支持,以及跨设备(cross-device)使用时会采用结合二维码与蓝牙近距离确认的混合方式。 ↩ ↩2 ↩3 ↩4 ↩5
-
W3C,Web Authentication: An API for accessing Public Key Credentials Level 2(W3C 推荐标准)。关于 WebAuthn 是用于创建・使用基于公钥加密的凭据的 API,凭据的私钥保存在认证器中,服务器(Relying Party)只会登记公钥与 credential ID,认证是通过对服务器发出的挑战值进行签名(断言)来完成的,以及凭据的作用域与保护方面的设计目标。同时,正文中也参照了该规范中关于 API 仅在安全上下文中公开、RP ID 省略时取调用方来源的有效域名、user handle(
user.id)是最长 64 字节的不透明值且不应包含用户名或邮箱地址等可识别个人的信息(§14.6.1 User Handle Contents)等内容。 ↩ ↩2 ↩3 ↩4 ↩5 -
W3C,Web Authentication Level 2 — §5.1.3 Create / RP ID validation, §13.4.9 Regarding phishing。关于公钥凭据的作用域被限定在 RP ID(Relying Party 标识符=域名)之内,浏览器(客户端)会核对调用方来源的可注册域名与 RP ID 是否对应,若不对应则拒绝创建・使用凭据,由此使得伪造来源无法访问其他网站专用的凭据,从而让 WebAuthn 具备包括中间人攻击在内的抗钓鱼能力。 ↩ ↩2 ↩3
-
CISA,Implementing Phishing-Resistant MFA(2022 年 10 月的说明文件)。关于使用短信、语音、推送通知、OTP 的 MFA 对钓鱼攻击、AiTM(中继)攻击、MFA 疲劳攻击均较为脆弱,抗钓鱼方式列举了 FIDO/WebAuthn 认证与基于 PKI 的认证(智能卡等)两种,其中 FIDO/WebAuthn 认证被定位为黄金标准,以及组织应首先从高风险账户开始迁移到抗钓鱼 MFA。 ↩ ↩2
-
W3C,Web Authentication Level 2 — §7.2 Verifying an Authentication Assertion。关于该规范中规定的服务器端验证步骤,包括对 clientDataJSON 的 type・challenge(与自己签发的值一致)・origin 进行验证,验证 authenticatorData 中的 rpIdHash 是否与期望的 RP ID 的 SHA-256 哈希一致,确认 User Present / User Verified 标志,以及用保存的公钥验证签名。 ↩ ↩2 ↩3
-
NIST,Incorporating Syncable Authenticators Into NIST SP 800-63B(2024 年 4 月的补遗,已并入 SP 800-63B 第 4 版)。关于可同步认证器(同步型通行密钥)的私钥,若以满足要求的形式保存・复制到同步结构中,即可满足 AAL2;另一方面,由于 AAL3 的加密认证器要求硬件保护・隔离的环境,因此私钥可导出的同步型认证器不得用于 AAL3;以及像 WebAuthn 这样进行来源验证的方式,被归类为具备验证者冒充抗性(抗钓鱼能力)。原文 PDF 见NIST SP 800-63B Supplement 1。 ↩ ↩2 ↩3 ↩4
-
Apple 支持,关于通行密钥的安全性。关于通行密钥通过 iCloud 钥匙串进行同步,iCloud 钥匙串采用端到端加密、连 Apple 自身也无法读取,同步受用户设备上的密钥保护,并提供了带有速率限制的托管(escrow)恢复机制。 ↩
-
Google,Google 密码管理器中通行密钥的安全性。关于通行密钥的私钥会在设备上加密后再同步,通过端到端加密使 Google 自身也无法访问私钥的内容,恢复时需要基于设备锁屏等方式的保护。 ↩
-
Microsoft Learn,在 Microsoft Entra ID 中启用 FIDO2 认证(通行密钥)。关于 Entra ID 支持通过 FIDO2 安全密钥以及 Microsoft Authenticator 的通行密钥(设备绑定)实现具备抗钓鱼能力的无密码认证,可以在认证方法策略中启用,并通过条件访问的认证强度(抗钓鱼 MFA)提出要求。 ↩ ↩2
-
Microsoft Learn,Windows 对通行密钥的支持。关于 Windows 11 支持使用 Windows Hello 创建・使用通行密钥,可以从设置 > 账户 > 通行密钥中管理已保存的通行密钥,在可使用 TPM 的环境中 Windows Hello 的凭据会受到硬件保护,以及可以通过二维码使用移动设备上的通行密钥。 ↩
相关文章
共享相同标签的最新文章。可以围绕相近的主题进一步加深理解。
图解 NTLM 与 Kerberos ── 为什么认证会「回退」到 NTLM
本文通过图解整理 NTLM 与 Kerberos 的区别,涵盖挑战/响应机制、TGT 与服务票据、SPN 无法解析时 Negotiate 回退到 NTLM 的条件、中继攻击与 Pass-the-Hash 得以成立的原因,直至 NTLMv1 被移除,并附有官方文档依据。
Windows 安全审核策略与事件日志排查实务 ── 成为看得懂 4625 的信息系统人员
这是一份用于应对「帮忙查一下登录失败日志」需求的实务指南。内容涵盖基本审核策略与高级审核策略的关系、至少应启用的子类别、事件 ID 4624/4625/4688 的解读方法、Security 日志的容量设计,直至用 Get-WinEvent 提取日志。
Windows LAPS实务指南 ── 告别全部PC通用的本地管理员密码
全部PC通用的本地管理员密码,是「一台被攻破、全部沦陷」的Pass-the-Hash攻击温床。本文讲解已成为OS标准功能的Windows LAPS如何自动轮换密码、如何配置保存到AD/Entra ID,以及运维中的常见陷阱。
Windows证书存储实务指南 ── 应该放入用户存储还是计算机存储
客户端证书究竟应该放入用户存储还是计算机存储?本文从 certmgr.msc 与 certlm.msc 的区别、私钥的权限授予,到 PowerShell 的到期日盘点,系统性地梳理证书相关的常见事故与对策,是一份实务指南。
Windows 防火墙与业务应用 ── 入站规则要通过安装程序注册
「开发机上正常运行,但在客户现场却无法通信」的常见原因就是 Windows 防火墙。本文讲解入站默认阻止与网络配置文件、不能把生产环境交给通知对话框处理的原因,以及通过安装程序注册入站规则的方法与排查步骤。
相关主题
与本文相近的主题页面。以本文为起点,可以进一步了解相关服务和其他文章。
Windows 技术主题
汇整 KomuraSoft LLC 关于 Windows 开发、故障调查与既有资产活用文章的主题中心。
常见问题
汇总了咨询这一主题时常见的问题。
- 通行密钥与密码在根本上有什么不同?
- 密码的机制是「用户与服务器共享同一个秘密,并在每次登录时发送这个秘密」。由于秘密存在于用户的脑海、输入框、通信路径、服务器数据库等所有地方,因此所有这些环节都会成为攻击目标。通行密钥使用的是公钥加密的密钥对,私钥不会被发送到服务器。在设备绑定型中,私钥完全不会从认证器中流出;即便在同步型中,也只会以端到端加密的形式流出到外部。服务器保存的只是公钥这种「即使泄露也无法被恶意利用的信息」,登录时发送的也只是针对当场一次性挑战值的签名。也就是说,密码的弱点所在——「共享的秘密」本身——根本不存在。此外,由于密钥对是按每个网站分别生成的,因此「重复使用」这个概念也无从谈起。
- 生物特征信息(指纹・人脸)会被发送到服务器吗?
- 不会。指纹或人脸数据只是在设备内部用于「打开装有私钥的保险柜」的本地核对手段,按照 FIDO 的设计,生物特征信息不会被发送到设备之外。服务器收到的,只是一个标记着「已完成用户本人验证(用户检验)」的签名,不仅不包含指纹本身,连其特征值也完全不包含。在无法使用生物识别的场景下可以用 PIN 代替,这个 PIN 与 Windows Hello 的 PIN 一样,也只在设备本地进行核对,不会在网络上传输,这一点与密码有着决定性的不同。
- 为什么通行密钥对钓鱼攻击有很强的抵抗力?
- 因为从机制上来说,用户压根就不需要识破假网站。通行密钥与网站的域名(RP ID)绑定在一起生成,浏览器只会把与「当前显示网站的域名」相对应的通行密钥列为候选。即便访问了外观与真网站极其相似的假域名,真网站专用的通行密钥也不会出现在选项中,用户根本无从被骗。此外,由于签名中写入了浏览器确认过的来源与 RP ID 的哈希,即便中继了签名,也会在真正服务器一侧的验证环节被拒绝。像密码或短信验证码那样「把真正的凭据输入到假网站里」这种事故,在结构上根本无法发生,这正是它与依赖训练或提醒注意的对策之间根本性的区别。
- 手机丢了会导致无法登录账户吗?
- 如果是同步型的通行密钥(保存在 iCloud 钥匙串或 Google 密码管理器中的那种),那么只要在登录了同一个 Apple ID/Google 账户的新设备上,就可以恢复。不过,恢复端到端加密的保管库,除了账户密码之外,还需要输入此前设备的锁屏密码等额外的本人验证,因此如果连这些找回手段也一并丢失,就可能无法恢复。不要把一切都只寄托在一台手机上,这种心态很重要。设备绑定型(安全密钥、Windows Hello 等)与设备命运与共,因此对于重要账户,惯常做法是提前注册多个通行密钥。许多服务都支持在一个账户上注册多个通行密钥。需要注意的是,丢失时的失效处理流程会因类型而异:同步型由于各设备上的副本都是同一份凭据,因此首先要在平台账户一侧删除丢失的设备或进行远程擦除,使设备上的副本无法再使用(在服务一侧的账户设置中删除该通行密钥,会一次性使所有设备上的副本失效)。而对于设备绑定型,只要在服务一侧删除该认证器的通行密钥,就只会使丢失的那把密钥失效。在组织中使用时,重要的是把「用户能够自行恢复的双重化机制」与「丢失时管理员能够予以吊销的流程」一并设计好。
- 通行密钥也有弱点吗?
- 有。不过更准确的说法是,弱点所在的位置发生了变化。由于认证本身借助公钥加密变得更加牢固,攻击者就会转而瞄准更薄弱的周边地带。具体来说包括:如果密码、短信等传统手段依然并存,那里就会作为最薄弱的环节残留下来;利用账户找回流程、诱导注册攻击者自己的通行密钥的手法;在同步型中,云账户本身被劫持会成为新的单点故障。此外,窃取登录后会话 Cookie 的攻击并不能被通行密钥防住,也就是说,与之无关的威胁并不会因此消失。导入时不能只满足于「加上通行密钥」,还需要把找回流程的加固,以及有计划地缩减后备手段一并纳入设计之中。