嘿,大家好!欢迎来到今天的“WebAuthn、Attestation、FIDO Metadata Service (MDS) 和 Trust Anchors:一场身份认证的狂欢”讲座!准备好迎接一波代码和概念轰炸了吗?
一、WebAuthn:无密码认证的派对入场券
WebAuthn,全称 Web Authentication API,是 W3C 推出的一个标准,旨在让用户摆脱对密码的依赖,拥抱更安全、更便捷的身份验证方式。你可以把它想象成一个超级VIP通行证,让你轻松进入各种网站和应用,还不用担心密码泄露的烦恼。
1.1 WebAuthn 的工作流程:一段浪漫的握手
WebAuthn 的核心在于公钥加密。简单来说,就是你的设备(比如手机、指纹识别器、安全密钥)会生成一对密钥:一个公钥和一个私钥。公钥会交给网站,私钥则安全地保存在你的设备里。
-
注册 (Registration):
- 网站发起注册请求,告诉你的浏览器:“嘿,我想让你用 WebAuthn 注册一下。”
- 浏览器会提示你选择一个身份验证器(比如指纹识别器)。
- 身份验证器会生成密钥对,并将公钥返回给浏览器。
- 浏览器将公钥发送给网站,网站保存这个公钥,并与你的用户账号关联起来。
// 客户端代码 (注册) async function register() { const options = { publicKey: { challenge: new Uint8Array([/* 随机 challenge,服务器生成 */]), rp: { name: "Your Website" // Relying Party (网站) 名称 }, user: { id: new Uint8Array([/* 用户ID */]), name: "[email protected]", displayName: "Awesome User" }, pubKeyCredParams: [ { type: "public-key", alg: -7 } // 算法 -7 代表 ES256 ], attestation: "direct" // 或者 "indirect" 或 "none" } }; try { const credential = await navigator.credentials.create({ publicKey: options.publicKey }); // 将 credential.response.attestationObject, credential.response.clientDataJSON, credential.rawId 发送给服务器 console.log(credential); return credential; } catch (error) { console.error("注册失败:", error); } } // 服务器端代码 (注册,简化版) async function handleRegistration(credential) { // 验证 challenge // 验证 origin // 验证 credential.response.clientDataJSON // 验证 attestationObject (非常重要,后面会详细讲) // 保存 credential.rawId 和 credential.response.attestationObject 里的公钥 // 将用户ID与公钥关联 }
-
认证 (Authentication):
- 当你再次访问网站时,网站会发起认证请求。
- 浏览器会提示你使用之前注册的身份验证器。
- 身份验证器会使用私钥对一个由网站生成的挑战 (challenge) 进行签名。
- 浏览器将签名后的挑战发送给网站。
- 网站使用之前保存的公钥验证签名,如果签名有效,则认证成功。
// 客户端代码 (认证) async function authenticate() { const options = { publicKey: { challenge: new Uint8Array([/* 随机 challenge,服务器生成 */]), allowCredentials: [ { id: new Uint8Array([/* credential ID,从服务器获取 */]), type: "public-key", transports: ["usb", "nfc", "ble"] // 可选,指定允许的传输方式 } ], userVerification: "preferred" // "required", "preferred", "discouraged" } }; try { const assertion = await navigator.credentials.get({ publicKey: options.publicKey }); // 将 assertion.response.authenticatorData, assertion.response.clientDataJSON, assertion.response.signature, assertion.rawId 发送给服务器 console.log(assertion); return assertion; } catch (error) { console.error("认证失败:", error); } } // 服务器端代码 (认证,简化版) async function handleAuthentication(assertion, credentialId) { // 获取与 credentialId 关联的公钥 // 验证 challenge // 验证 origin // 验证 assertion.response.clientDataJSON // 验证 assertion.response.authenticatorData // 使用公钥验证 assertion.response.signature // 如果验证成功,则认证通过 }
1.2 什么是 Challenge?
Challenge 是一个由服务器生成的随机数,用于防止重放攻击。每次注册或认证时,服务器都会生成一个新的 challenge,并确保浏览器返回的签名是基于这个 challenge 生成的。如果没有 challenge,攻击者可以截获之前的签名,并重复使用它来冒充用户。
二、Attestation:验证身份验证器的真伪
Attestation 是 WebAuthn 中一个至关重要的概念。它允许网站验证你使用的身份验证器是否是可信的,以及它是否由合法的制造商生产。这就像检查你的 VIP 通行证是否是伪造的,以确保只有真正的 VIP 才能进入派对。
2.1 Attestation 的工作原理:证据链
Attestation 的过程涉及到一系列的证书和签名。
- 身份验证器生成 Attestation 证书: 在生产过程中,身份验证器会生成一个 Attestation 证书,这个证书由制造商的私钥签名。
- Attestation 对象: 当你注册 WebAuthn 时,身份验证器会创建一个 Attestation 对象,其中包含了 Attestation 证书和一些关于身份验证器的信息(比如制造商 ID、型号)。
- 网站验证 Attestation 对象: 网站会验证 Attestation 证书的有效性,并检查它是否由可信的制造商签名。
2.2 Attestation 语句格式:
Attestation 对象中包含一个 Attestation 语句,它描述了如何验证身份验证器的真实性。常见的 Attestation 语句格式有:
- FIDO U2F: 一种早期的认证协议,Attestation 语句通常包含一个 U2F 证书。
- Packed: 一种通用的 Attestation 格式,Attestation 语句包含一个 X.509 证书链。
- TPM: 用于 TPM (Trusted Platform Module) 芯片的 Attestation 格式。
- Android SafetyNet: 用于 Android 设备的 Attestation 格式,依赖于 Google 的 SafetyNet API。
- None: 没有 Attestation,网站无法验证身份验证器的真实性。(不推荐)
2.3 代码示例 (解析 Attestation 对象):
// 一个简化的解析 attestationObject 的示例
function parseAttestationObject(attestationObject) {
const decoded = CBOR.decode(attestationObject); // 假设使用了 CBOR 解码库
const fmt = decoded.fmt; // Attestation 格式 (例如 "packed", "fido-u2f")
const attStmt = decoded.attStmt; // Attestation 语句
if (fmt === "packed") {
const sig = attStmt.sig; // 签名
const alg = attStmt.alg; // 签名算法
const x5c = attStmt.x5c; // X.509 证书链
// TODO: 验证证书链,并验证签名
console.log("Packed Attestation:", { sig, alg, x5c });
} else if (fmt === "fido-u2f") {
const sig = attStmt.sig;
const x5c = attStmt.x5c;
// TODO: 验证 U2F 证书,并验证签名
console.log("FIDO U2F Attestation:", { sig, x5c });
} else {
console.warn("不支持的 Attestation 格式:", fmt);
}
return decoded;
}
// 假设 credential.response.attestationObject 是 ArrayBuffer
const attestationObject = credential.response.attestationObject;
const parsedAttestation = parseAttestationObject(attestationObject);
三、FIDO Metadata Service (MDS):身份验证器的百科全书
FIDO Metadata Service (MDS) 是 FIDO 联盟维护的一个数据库,其中包含了关于各种 FIDO 认证身份验证器的元数据信息。你可以把它想象成一本身份验证器的百科全书,里面记录了每个身份验证器的制造商、型号、功能、安全级别等信息。
3.1 MDS 的作用:信任的基石
MDS 的主要作用是帮助网站评估身份验证器的可信度。通过查询 MDS,网站可以:
- 验证制造商的身份: 确认身份验证器是否由 FIDO 认证的制造商生产。
- 了解身份验证器的功能: 确定身份验证器支持哪些功能(比如指纹识别、NFC、蓝牙)。
- 评估身份验证器的安全级别: 了解身份验证器是否通过了安全认证,以及它是否容易受到攻击。
- 检查身份验证器是否被撤销: 确认身份验证器是否因为安全问题而被制造商或 FIDO 联盟撤销。
3.2 MDS 的数据结构:
MDS 的数据以 JSON 格式存储,包含以下主要字段:
legalHeader
: 法律声明no
: 序列号,每次更新都会递增nextUpdate
: 下次更新的时间entries
: 一个数组,包含多个身份验证器的元数据条目
每个元数据条目包含以下字段:
aaid
: 身份验证器 Attestation ID,用于唯一标识一个身份验证器型号。aaguid
: 身份验证器 Attestation GUID,用于唯一标识一个身份验证器实例。(注意AAID和AAGUID的区别)metadataStatement
: 包含身份验证器的详细信息。statusReports
: 包含身份验证器的状态报告(比如是否被撤销)。timeOfLastStatusChange
: 上次状态改变的时间。
3.3 代码示例 (从 MDS 获取元数据):
// 假设你已经从 FIDO MDS 获取了 metadataBlob
// 并已经验证了 metadataBlob 的签名 (后面会讲 Trust Anchors)
async function getMetadata(aaid) {
// 解码 metadataBlob (通常是 base64 编码的 JWT)
const decodedMetadata = JSON.parse(atob(metadataBlob.split(".")[1])); // 简化版,不处理错误
const entries = decodedMetadata.entries;
const metadataEntry = entries.find(entry => entry.aaid === aaid);
if (metadataEntry) {
console.log("找到元数据:", metadataEntry.metadataStatement);
return metadataEntry.metadataStatement;
} else {
console.warn("找不到 AAID 为", aaid, "的元数据");
return null;
}
}
// 假设 parsedAttestation.attStmt.attCertInfo.aaguid 包含了 aaguid
const aaid = parsedAttestation.attStmt.attCertInfo.aaguid;
const metadata = await getMetadata(aaid);
if (metadata) {
// 根据元数据进行进一步的验证和处理
console.log("身份验证器制造商:", metadata.manufacturerName);
console.log("身份验证器型号:", metadata.modelName);
// 检查是否被撤销
// 检查安全级别
}
四、Trust Anchors:信任的根基
Trust Anchors 是指受信任的根证书颁发机构 (CA) 的证书。在 WebAuthn 的上下文中,Trust Anchors 用于验证 FIDO Metadata Service (MDS) 发布的元数据签名,确保你从 MDS 获取的数据是真实可靠的。你可以把它想象成政府颁发的公章,确保 MDS 发布的信息是官方的、权威的。
4.1 Trust Anchors 的作用:防止中间人攻击
如果没有 Trust Anchors,攻击者可以伪造 MDS 服务器,并向网站提供虚假的元数据。通过使用 Trust Anchors 验证 MDS 的签名,网站可以确保它们与真正的 MDS 服务器通信,并获取真实的元数据。
4.2 Trust Anchors 的来源:
FIDO 联盟会定期发布 Trust Anchors,你可以从 FIDO 联盟的官方网站下载。Trust Anchors 通常以 PEM 格式存储,包含根证书的公钥信息。
4.3 代码示例 (验证 MDS 签名):
const fs = require('fs');
const jwt = require('jsonwebtoken');
const jwkToPem = require('jwk-to-pem');
// 假设你已经下载了 FIDO 联盟的 Trust Anchors (root.pem)
const trustAnchor = fs.readFileSync('root.pem', 'utf8');
// 假设你已经从 FIDO MDS 获取了 metadataBlob (JWT 格式)
function verifyMdsSignature(metadataBlob) {
try {
const decodedHeader = jwt.decode(metadataBlob, { complete: true }).header;
const key = {
kty: 'RSA',
n: decodedHeader.x5c[0], // 假设 x5c[0] 是 JWT Header 中的 RSA 模数 (n)
e: decodedHeader.x5c[1] // 假设 x5c[1] 是 JWT Header 中的 RSA 指数 (e)
};
const publicKey = jwkToPem(key);
const verified = jwt.verify(metadataBlob, publicKey, { algorithms: ['RS256'] }); // 或者其他算法
console.log("MDS 签名验证成功");
return verified;
} catch (error) {
console.error("MDS 签名验证失败:", error);
return null;
}
}
const verifiedMetadata = verifyMdsSignature(metadataBlob);
if (verifiedMetadata) {
// 现在你可以安全地使用 verifiedMetadata 中的数据了
console.log("MDS 版本:", verifiedMetadata.no);
}
五、WebAuthn 安全考量:别让你的派对被捣乱
WebAuthn 提供了比传统密码认证更高的安全性,但仍然需要注意一些安全问题:
- Challenge 的随机性: 确保 challenge 是真正的随机数,并且足够长,以防止被破解。
- Origin 验证: 验证请求的来源 (origin) 是否与你的网站域名一致,防止跨站请求伪造 (CSRF) 攻击。
- Attestation 验证: 始终验证 Attestation 对象,确保你使用的身份验证器是可信的。
- MDS 查询: 定期查询 MDS,检查身份验证器是否被撤销,并更新元数据信息。
- 用户验证策略: 根据应用的安全需求,选择合适的用户验证策略 (userVerification),例如 "required"、"preferred" 或 "discouraged"。
- 传输方式选择: 考虑支持的传输方式 (transports),例如 USB、NFC、蓝牙,并根据用户的设备和安全需求进行选择。
- 密钥泄露: 即使 WebAuthn 使用公钥加密,私钥仍然需要安全地存储在用户的设备上。如果私钥泄露,攻击者可以冒充用户。
六、总结:WebAuthn 的未来
WebAuthn 正在迅速普及,成为无密码认证的未来。通过理解 WebAuthn、Attestation、FIDO Metadata Service (MDS) 和 Trust Anchors 的工作原理,你可以构建更安全、更便捷的身份验证系统,为用户提供更好的体验。
表格:WebAuthn 相关概念总结
概念 | 描述 | 作用 |
---|---|---|
WebAuthn | W3C 推出的无密码认证标准 | 提供更安全、更便捷的身份验证方式,取代密码 |
Attestation | 验证身份验证器的真实性 | 确保身份验证器是由可信的制造商生产,防止使用伪造的身份验证器 |
FIDO Metadata Service (MDS) | 包含各种 FIDO 认证身份验证器的元数据信息 | 帮助网站评估身份验证器的可信度,了解身份验证器的功能和安全级别,检查是否被撤销 |
Trust Anchors | 受信任的根证书颁发机构 (CA) 的证书 | 验证 FIDO Metadata Service (MDS) 发布的元数据签名,确保从 MDS 获取的数据是真实可靠的,防止中间人攻击 |
Challenge | 服务器生成的随机数 | 防止重放攻击,确保每次注册或认证都是唯一的 |
Relying Party | 依赖 WebAuthn 进行身份验证的网站或应用 | 负责发起注册和认证请求,验证用户的身份 |
Authenticator | 身份验证器,例如指纹识别器、安全密钥 | 负责生成密钥对,对 challenge 进行签名 |
AAID | 身份验证器 Attestation ID,用于唯一标识一个身份验证器型号 | 用于在 MDS 中查找特定型号的身份验证器的元数据 |
AAGUID | 身份验证器 Attestation GUID,用于唯一标识一个身份验证器实例 | 一些身份验证器使用 AAGUID 代替 AAID。两者目的相同,都是为了在 MDS 中查找元数据。 |
希望这次讲座能帮助你更好地理解 WebAuthn 的世界。记住,安全是一个持续的过程,需要不断学习和改进。下次再见!