FIDO2 или WebAuthn — вопрос не о выборе, а об уровнях. WebAuthn (W3C Web Authentication) определяет JavaScript API, который вызывают доверяющие стороны (relying parties, RP), и форматы подписанных данных, которые они проверяют. CTAP (FIDO Client to Authenticator Protocol) определяет, как браузер или операционная система обменивается данными с внешним аутентификатором через USB, NFC, Bluetooth или гибридный транспорт с QR-кодом. FIDO2 — общее название для связки WebAuthn и CTAP 2, которое FIDO Alliance ввёл в 2018 году. Ключи доступа (passkeys) — это учётные данные FIDO, созданные по этим стандартам.
Коротко о главном
| WebAuthn | CTAP | FIDO2 | |
|---|---|---|---|
| Что это | API браузера и форматы данных | Протокол обмена с внешними аутентификаторами | Общее название для обоих |
| Кто выпускает | W3C | FIDO Alliance | FIDO Alliance |
| Текущая версия | Level 3, рекомендация W3C от 25 авг. 2026 г. | CTAP 2.3, предлагаемый стандарт от 26 февр. 2026 г. (2.2: июль 2025 г.) | WebAuthn Level 3 с CTAP 2.3 |
| Прежние версии | Level 1 (март 2019 г.), Level 2 (апрель 2021 г.) | CTAP 2.0, 2.1 (2021 г.); CTAP1 — это U2F | Предшественник — U2F (2014 г.) |
| Кто реализует | Браузеры и платформы (API), доверяющие стороны (проверки на сервере) | Браузеры, операционные системы, ключи безопасности, телефоны | Все участники цепочки |
| Связывает | Веб-страницу и браузер; сервер и данные аутентификатора | Клиентскую платформу и аутентификатор | Доверяющую сторону с аутентификатором |
| Кодирование | Объекты JavaScript, clientDataJSON, объекты аттестации в CBOR | Команды CTAP2 в каноническом CBOR, например authenticatorGetAssertion (0x02) | Оба варианта |
| Транспорты | Не применяется | USB HID, NFC, Bluetooth Low Energy, гибридный (QR-код, проверка близости, туннель) | Оба варианта |
| Основные точки входа | navigator.credentials.create() и .get() | authenticatorMakeCredential (0x01), authenticatorGetInfo (0x04) | Не применяется |
Кто что делает при входе
- Сайт вызывает
navigator.credentials.get(), передавая challenge и свой идентификатор доверяющей стороны (RP ID). Это WebAuthn. - Браузер собирает
clientDataJSON(тип, challenge, источник, флаг crossOrigin), хеширует его и просит аутентификатор поставить подпись. Для встроенного платформенного аутентификатора (Touch ID, Windows Hello, Android) запрос идёт через API операционной системы, а для ключа безопасности или телефона — по CTAP, в виде командыauthenticatorGetAssertionв CBOR. - Аутентификатор проверяет присутствие пользователя и, если это запрошено, выполняет проверку пользователя, а затем подписывает свои данные аутентификатора, к которым приписан хеш клиентских данных. Ответ возвращается по CTAP или через API платформы.
- Браузер возвращает эти байты странице, а сервер проверяет их так, как предписывает § 7.2 спецификации WebAuthn.
Скрипт ниже разбирает официальный тестовый вектор WebAuthn Level 3 из § 16.2, в котором есть регистрация и аутентификация для учётных данных ES256, и проверяет подпись (Node.js 26, cbor-x 1.6.0). authData и подпись созданы аутентификатором, а clientDataJSON — браузером.
import { createHash, createPublicKey, verify } from "node:crypto";
import { decode } from "cbor-x";
// WebAuthn Level 3 §16.2 test vector: ES256 credential, "none" attestation
const hex = (s) => Buffer.from(s, "hex");
const attestationObject = hex("a363666d74646e6f6e656761747453746d74a068617574684461746158a4bfabc37432958b063360d3ad6461c9c4735ae7f8edd46592a5e0f01452b2e4b559000000008446ccb9ab1db374750b2367ff6f3a1f0020f91f391db4c9b2fde0ea70189cba3fb63f579ba6122b33ad94ff3ec330084be4a5010203262001215820afefa16f97ca9b2d23eb86ccb64098d20db90856062eb249c33a9b672f26df61225820930a56b87a2fca66334b03458abf879717c12cc68ed73290af2e2664796b9220");
const authenticatorData = hex("bfabc37432958b063360d3ad6461c9c4735ae7f8edd46592a5e0f01452b2e4b51900000000");
const clientDataJSON = hex("7b2274797065223a22776562617574686e2e676574222c226368616c6c656e6765223a224f63446e55685158756c5455506f334a5558543049393770767a7a59425039745a63685879617630314167222c226f726967696e223a2268747470733a2f2f6578616d706c652e6f7267222c2263726f73734f726967696e223a66616c73657d");
const signature = hex("3046022100f50a4e2e4409249c4a853ba361282f09841df4dd4547a13a87780218deffcd380221008480ac0f0b93538174f575bf11a1dd5d78c6e486013f937295ea13653e331e87");
const sha256 = (b) => createHash("sha256").update(b).digest();
const flagNames = { 0x01: "UP", 0x04: "UV", 0x08: "BE", 0x10: "BS", 0x40: "AT", 0x80: "ED" };
const flags = (b) => Object.entries(flagNames).filter(([bit]) => b & bit).map(([, n]) => n).join(" ");
// Registration: authenticator data produced by the authenticator (CTAP authenticatorMakeCredential)
const { fmt, authData } = decode(attestationObject);
const credIdLen = authData.readUInt16BE(53);
const coseKey = decode(authData.subarray(55 + credIdLen));
console.log("attestation format:", fmt);
console.log("rpIdHash == SHA-256('example.org'):", authData.subarray(0, 32).equals(sha256("example.org")));
console.log("flags:", flags(authData[32]), "| signCount:", authData.readUInt32BE(33));
console.log("AAGUID:", authData.subarray(37, 53).toString("hex"), "| credential ID bytes:", credIdLen);
console.log("COSE key: kty", coseKey[1], "alg", coseKey[3], "crv", coseKey[-1]);
// Authentication: verify the assertion with the registered public key
const publicKey = createPublicKey({ key: { kty: "EC", crv: "P-256",
x: Buffer.from(coseKey[-2]).toString("base64url"), y: Buffer.from(coseKey[-3]).toString("base64url") }, format: "jwk" });
console.log("\nclientDataJSON (built by the browser):", clientDataJSON.toString());
console.log("assertion flags:", flags(authenticatorData[32]));
const ok = verify("sha256", Buffer.concat([authenticatorData, sha256(clientDataJSON)]), publicKey, signature);
console.log("signature over authenticatorData || SHA-256(clientDataJSON):", ok ? "valid" : "invalid");
attestation format: none
rpIdHash == SHA-256('example.org'): true
flags: UP BE BS AT | signCount: 0
AAGUID: 8446ccb9ab1db374750b2367ff6f3a1f | credential ID bytes: 32
COSE key: kty 2 alg -7 crv 1
clientDataJSON (built by the browser): {"type":"webauthn.get","challenge":"OcDnUhQXulTUPo3JUXT0I97pvzzYBP9tZchXyav01Ag","origin":"https://example.org","crossOrigin":false}
assertion flags: UP BE BS
signature over authenticatorData || SHA-256(clientDataJSON): valid
Флаги показывают присутствие пользователя (UP), учётные данные, для которых резервное копирование допускается и уже выполнено (BE, BS: синхронизируемый ключ доступа), и сведения об аттестованных учётных данных (AT) при регистрации. Ключ COSE (тип ключа 2 = EC2, алгоритм −7 = ES256, кривая 1 = P-256) — формат, общий для обеих спецификаций; инспектор WebAuthn декодирует те же структуры из настоящего ответа браузера.
От U2F к ключам доступа
Стандарт FIDO U2F (2014) дал ключам безопасности простой протокол второго фактора, который теперь называется CTAP1, и API для браузера. В 2018–2019 годах на смену этому API пришёл WebAuthn (Chrome 67, Firefox 60, Safari 13), а CTAP 2 добавил PIN-коды, встроенную проверку пользователя и обнаруживаемые учётные данные, так что ключ смог заменить пароль, а не только дополнять его. Из Chrome старый JavaScript API для U2F удалили в версии 104 (2022 год); учётные данные, зарегистрированные через него, по-прежнему работают в WebAuthn благодаря расширению appid.
Когда Apple, Google и Microsoft в 2022 году объявили о синхронизируемых ключах доступа, нового протокола для этого не понадобилось: ключ доступа — это обнаруживаемые учётные данные с установленными флагами резервного копирования, создаваемые и используемые через тот же API WebAuthn. CTAP 2.2 (июль 2025 года) стандартизировал политики сложности PIN-кода и гибридный транспорт, который позволяет телефону после сканирования QR-кода работать аутентификатором для компьютера поблизости; CTAP 2.3 (февраль 2026 года) — очередная редакция с небольшими изменениями: например, в ней аутентификаторы могут сообщать, какие команды конфигурации они поддерживают.
Аттестация и сертификация
При регистрации аутентификатор может приложить заявление об аттестации, подписанное ключом, общим для целой партии устройств, и AAGUID, который указывает модель. WebAuthn определяет форматы этих заявлений (packed, tpm, android-key, apple, fido-u2f, none), а FIDO Alliance сертифицирует аутентификаторы и публикует их метаданные, включая корневые сертификаты аттестации и уровни сертификации, в службе метаданных FIDO (FIDO Metadata Service). Большинству сайтов для массовой аудитории не следует запрашивать аттестацию, а принимать следует любой аутентификатор. Компании, которые обязаны допускать только сертифицированные аппаратные ключи, запрашивают аттестацию direct или enterprise и сверяют её с метаданными; как это работает, показывают проверка аттестации YubiKey и поиск по AAGUID.
Что реализовать
- Сайт или веб-приложение: WebAuthn в браузере и серверная библиотека, которая реализует шаги проверки из § 7.
- Нативное приложение для iOS или Android: API ключей доступа платформы (AuthenticationServices, Credential Manager), которые возвращают данные в формате WebAuthn, привязанные к вашему веб-домену через associated domains или Digital Asset Links.
- Компания, которая допускает только аппаратные ключи: WebAuthn с аттестацией, которую сверяют с записями службы метаданных FIDO.
- Производитель ключей безопасности, телефонов или ОС: CTAP для внешних аутентификаторов и сертификация FIDO для продукта.
- Закупки: запрашивайте уровень сертификации FIDO, поддерживаемую версию CTAP и транспорты, а также возможности WebAuthn, на которые вы опираетесь, а не просто «поддержку FIDO2».
Заметки о миграции
- Замена регистраций U2F: при входе продолжайте передавать старый App ID через расширение
appid, а при следующем входе пользователя регистрируйте для него новые учётные данные WebAuthn. - Переход на ключи доступа: запрашивайте
residentKey: "required"иuserVerification: "preferred"или"required"и сохраняйте флаги резервного копирования. - Переход на возможности Level 3 выполняйте постепенно, по одной: условное создание,
getClientCapabilities(), сигнальные методы и связанные источники (related origins) — все они корректно деградируют там, где их не поддерживают. - Тестирование: до запуска проверьте в песочнице ключей доступа платформенные аутентификаторы, ключ USB или NFC и гибридный сценарий с телефоном.
Источники
- W3C Web Authentication Level 3 (данные аутентификатора в § 6.1, операции доверяющей стороны в § 7, тестовые векторы в § 16)
- Протокол связи клиента с аутентификатором FIDO (CTAP) 2.3
- Протокол связи клиента с аутентификатором FIDO (CTAP) 2.2 (команды в § 6, привязки к транспортам в § 11)
- Служба метаданных FIDO (FIDO Metadata Service)
- RFC 9052, структуры и обработка COSE и RFC 9053, алгоритмы COSE