Пароли делают учетные записи пользователей безопаснее, проще и удобнее в использовании.
Использование паролей повышает безопасность, упрощает вход в систему и заменяет пароли. В отличие от обычных паролей, которые пользователи должны запоминать и вводить вручную, пароли используют механизмы блокировки экрана устройства, такие как биометрия или PIN-коды, и снижают риски фишинга и кражи учетных данных.
Пароли синхронизируются между устройствами с помощью таких поставщиков паролей, как Google Password Manager и iCloud Keychain.
Необходимо создать ключ доступа, надежно сохранив закрытый ключ у поставщика ключей доступа вместе с необходимыми метаданными и его открытым ключом, хранящимся на вашем сервере для аутентификации. Закрытый ключ выдает подпись после проверки пользователя на допустимом домене, что делает ключи доступа устойчивыми к фишингу. Открытый ключ проверяет подпись без сохранения конфиденциальных учетных данных, что делает ключи доступа устойчивыми к краже учетных данных.
Как работает создание ключа доступа
Прежде чем пользователь сможет войти в систему с помощью ключа доступа, вам следует создать ключ доступа, связать его с учетной записью пользователя и сохранить его открытый ключ на вашем сервере.
Вы можете попросить пользователей создать пароль в одной из следующих ситуаций:
- Во время или после регистрации.
- После входа в систему.
- После входа с использованием ключа доступа с другого устройства (то есть
[authenticatorAttachment](https://web.dev/articles/passkey-form-autofill#authenticator-attachment)
являетсяcross-platform
). - На специальной странице пользователи могут управлять своими паролями.
Для создания ключа доступа используется API WebAuthn .
Четыре компонента процесса регистрации ключа доступа:
- Бэкэнд : хранит данные учетной записи пользователя, включая открытый ключ.
- Фронтенд : взаимодействует с браузером и извлекает необходимые данные из бэкенда.
- Браузер : запускает ваш JavaScript и взаимодействует с API WebAuthn.
- Поставщик ключа доступа : создает и хранит ключ доступа. Обычно это менеджер паролей, такой как Google Password Manager, или ключ безопасности.

Перед созданием ключа доступа убедитесь, что система соответствует следующим предварительным условиям:
Учетная запись пользователя проверяется безопасным способом (например, по электронной почте, телефону или с помощью федерации удостоверений) в течение достаточно короткого периода времени.
Фронтенд и бэкенд могут безопасно взаимодействовать для обмена учетными данными.
Браузер поддерживает WebAuthn и создание ключа доступа.
В следующих разделах мы покажем вам, как проверить большинство из них.
Как только система выполнит эти условия, произойдет следующий процесс создания ключа доступа:
- Система запускает процесс создания ключа доступа, когда пользователь инициирует действие (например, нажимает кнопку «Создать ключ доступа» на своей странице управления ключами доступа или после завершения регистрации).
- Фронтенд запрашивает необходимые учетные данные у бэкенда, включая информацию о пользователе, запрос и идентификаторы учетных данных для предотвращения дублирования.
- Фронтенд вызывает
navigator.credentials.create()
чтобы побудить поставщика ключа доступа устройства сгенерировать ключ доступа, используя информацию из бэкенда. Обратите внимание, что этот вызов возвращает обещание. - Устройство пользователя аутентифицирует его, используя биометрический метод, PIN-код или шаблон для создания ключа доступа.
- Поставщик ключа доступа создает ключ доступа и возвращает открытый ключ доступа на интерфейс, выполняя обещание.
- Фронтенд отправляет сгенерированные учетные данные открытого ключа на бэкенд.
- Бэкэнд хранит открытый ключ и другие важные данные для будущей аутентификации.
- Бэкэнд уведомляет пользователя (например, по электронной почте) о необходимости подтвердить создание ключа доступа и обнаружить потенциальный несанкционированный доступ.
Этот процесс обеспечивает безопасный и бесперебойный процесс регистрации ключа доступа для пользователей.
Совместимость
Большинство браузеров поддерживают WebAuthn, с некоторыми небольшими пробелами. Смотрите passkeys.dev для подробностей о совместимости браузеров и ОС.
Создать новый пароль
Чтобы создать новый ключ доступа, интерфейс должен выполнить следующие действия:
- Проверьте совместимость.
- Извлечение информации из бэкэнда.
- Вызовите API WebAuth для создания ключа доступа.
- Отправьте возвращенный открытый ключ на бэкэнд.
- Сохраните учетные данные.
В следующих разделах показано, как это можно сделать.
Проверить совместимость
Перед отображением кнопки «Создать новый ключ доступа» интерфейс должен проверить следующее:
- Браузер поддерживает WebAuthn с
PublicKeyCredential
.
- Устройство поддерживает аутентификатор платформы (может создать ключ доступа и выполнить аутентификацию с его помощью) с помощью
PublicKeyCredential.isUserVerifyingPlatformAuthenticatorAvailable()
.
- Браузер поддерживает условный пользовательский интерфейс WebAuthn с
PublicKeyCredenital.isConditionalMediationAvailable()
.
В следующем фрагменте кода показано, как можно проверить совместимость перед отображением параметров, связанных с ключом доступа.
// Availability of `window.PublicKeyCredential` means WebAuthn is usable.
// `isUserVerifyingPlatformAuthenticatorAvailable` means the feature detection is usable.
// `isConditionalMediationAvailable` means the feature detection is usable.
if (window.PublicKeyCredential &&
PublicKeyCredential.isUserVerifyingPlatformAuthenticatorAvailable &&
PublicKeyCredential.isConditionalMediationAvailable) {
// Check if user verifying platform authenticator is available.
Promise.all([
PublicKeyCredential.isUserVerifyingPlatformAuthenticatorAvailable(),
PublicKeyCredential.isConditionalMediationAvailable(),
]).then(results => {
if (results.every(r => r === true)) {
// Display "Create a new passkey" button
}
});
}
В этом примере кнопка «Создать новый ключ доступа» должна отображаться только при соблюдении всех условий.
Извлечение информации из бэкэнда
Когда пользователь нажимает кнопку, извлеките необходимую информацию из бэкэнда для вызова navigator.credentials.create()
.
В следующем фрагменте кода показан объект JSON с необходимой информацией для вызова navigator.credentials.create()
:
// Example `PublicKeyCredentialCreationOptions` contents
{
challenge: *****,
rp: {
name: "Example",
id: "example.com",
},
user: {
id: *****,
name: "john78",
displayName: "John",
},
pubKeyCredParams: [{
alg: -7, type: "public-key"
},{
alg: -257, type: "public-key"
}],
excludeCredentials: [{
id: *****,
type: 'public-key',
transports: ['internal'],
}],
authenticatorSelection: {
authenticatorAttachment: "platform",
requireResidentKey: true,
}
}
Пары ключ-значение в объекте содержат следующую информацию:
-
challenge
: сгенерированный сервером вызов в ArrayBuffer для этой регистрации. -
rp.id
: идентификатор RP (идентификатор проверяющей стороны), домен и веб-сайт могут указывать либо свой домен, либо регистрируемый суффикс. Например, если происхождение RP —https://login.example.com:1337
, идентификатор RP может бытьlogin.example.com
илиexample.com
. Если идентификатор RP указан какexample.com
, пользователь может пройти аутентификацию наlogin.example.com
или на любых поддоменах наexample.com
. См. Разрешить повторное использование ключа доступа на ваших сайтах с помощью связанных запросов источника для получения дополнительной информации об этом. -
rp.name
: Имя RP (Relying Party). Это устарело в WebAuthn L3, но включено для совместимости. -
user.id
: Уникальный идентификатор пользователя в ArrayBuffer, сгенерированный при создании учетной записи. Он должен быть постоянным, в отличие от имени пользователя, которое можно редактировать. Идентификатор пользователя идентифицирует учетную запись, но не должен содержать никакой персонально идентифицируемой информации (PII) . Вероятно, у вас уже есть идентификатор пользователя в вашей системе, но при необходимости создайте его специально для паролей, чтобы он не содержал никакой PII. -
user.name
: Уникальный идентификатор учетной записи, который пользователь узнает, например, адрес электронной почты или имя пользователя. Это будет отображаться в селекторе учетных записей. -
user.displayName
: Обязательное, более удобное для пользователя имя для учетной записи. Оно не обязательно должно быть уникальным и может быть выбранным пользователем именем. Если на вашем сайте нет подходящего значения для включения сюда, передайте пустую строку. Это может отображаться в селекторе учетных записей в зависимости от браузера. -
pubKeyCredParams
: Указывает поддерживаемые RP (проверяющей стороной) алгоритмы открытого ключа. Мы рекомендуем установить его на[{alg: -7, type: "public-key"},{alg: -257, type: "public-key"}]
. Это указывает поддержку ECDSA с P-256 и RSA PKCS#1, и поддержка этих алгоритмов обеспечивает полное покрытие. -
excludeCredentials
: Список уже зарегистрированных идентификаторов учетных данных. Предотвращает регистрацию одного и того же устройства дважды, предоставляя список уже зарегистрированных идентификаторов учетных данных . Членtransports
, если он указан, должен содержать результат вызоваgetTransports()
во время регистрации каждого удостоверения. -
authenticatorSelection.authenticatorAttachment
: Установите это на"platform"
вместе сhint: ['client-device']
если это создание ключа доступа является обновлением пароля, например, в рамках акции после входа в систему."platform"
указывает, что RP хочет аутентификатор платформы (аутентификатор, встроенный в устройство платформы), который не предлагает, например, вставить ключ безопасности USB. У пользователя есть более простой вариант создания ключа доступа. -
authenticatorSelection.requireResidentKey
: Установите его в логическое значениеtrue
. Обнаруживаемые учетные данные (резидентный ключ) сохраняют информацию о пользователе в ключе доступа и позволяют пользователям выбирать учетную запись при аутентификации. authenticatorSelection.userVerification
: Указывает, является ли проверка пользователя с помощью блокировки экрана устройства"required"
,"preferred"
или"discouraged"
. Значение по умолчанию —"preferred"
, что означает, что аутентификатор может пропустить проверку пользователя. Установите значение"preferred"
или опустите свойство.
Мы рекомендуем создать объект на сервере, кодируя ArrayBuffer с Base64URL и извлекая его из frontend. Таким образом, вы можете декодировать полезную нагрузку с помощью PublicKeyCredential.parseCreationOptionsFromJSON()
и передавать ее напрямую в navigator.credentials.create()
.
В следующем фрагменте кода показано, как можно извлечь и декодировать информацию, необходимую для создания ключа доступа.
// Fetch an encoded `PubicKeyCredentialCreationOptions` from the server.
const _options = await fetch('/webauthn/registerRequest');
// Deserialize and decode the `PublicKeyCredentialCreationOptions`.
const decoded_options = JSON.parse(_options);
const options = PublicKeyCredential.parseCreationOptionsFromJSON(decoded_options);
...
Вызовите API WebAuthn для создания ключа доступа
Вызовите navigator.credentials.create()
для создания нового ключа доступа. API возвращает обещание, ожидая взаимодействия пользователя, отображающего модальный диалог.
// Invoke WebAuthn to create a passkey.
const credential = await navigator.credentials.create({
publicKey: options
});
Отправьте возвращенные учетные данные открытого ключа на бэкэнд.
После проверки пользователя с помощью блокировки экрана устройства создается ключ доступа, и обещание разрешается, возвращая объект PublicKeyCredential во внешний интерфейс.
Обещание может быть отклонено по разным причинам. Вы можете обработать эти ошибки, проверив свойство name
объекта Error
:
-
InvalidStateError
: Пароль уже существует на устройстве. Диалоговое окно с сообщением об ошибке не будет показано пользователю. Сайт не должен рассматривать это как ошибку. Пользователь хотел зарегистрировать локальное устройство, и оно зарегистрировано. -
NotAllowedError
: Пользователь отменил операцию. -
AbortError
: Операция была прервана. - Другие исключения : Произошло что-то неожиданное. Браузер показывает пользователю диалоговое окно с ошибкой.
Объект учетных данных открытого ключа содержит следующие свойства:
-
id
: закодированный в Base64URL идентификатор созданного ключа доступа. Этот идентификатор помогает браузеру определить, есть ли соответствующий ключ доступа на устройстве при аутентификации. Это значение должно храниться в базе данных на бэкэнде. -
rawId
: версия ArrayBuffer идентификатора учетных данных. -
response.clientDataJSON
: данные клиента, закодированные в ArrayBuffer. -
response.attestationObject
: закодированный объект подтверждения ArrayBuffer. Он содержит важную информацию, такую как идентификатор RP, флаги и открытый ключ. -
authenticatorAttachment
: возвращает"platform"
если эти учетные данные созданы на устройстве с поддержкой ключа доступа. -
type
: это поле всегда имеет значение"public-key"
.
Закодируйте объект с помощью метода .toJSON()
, сериализуйте его с помощью JSON.stringify()
а затем отправьте на сервер.
...
// Encode and serialize the `PublicKeyCredential`.
const _result = credential.toJSON();
const result = JSON.stringify(_result);
// Encode and send the credential to the server for verification.
const response = await fetch('/webauthn/registerResponse', {
method: 'post',
credentials: 'same-origin',
body: result
});
...
Сохраните учетные данные
После получения учетных данных открытого ключа на бэкэнде мы рекомендуем использовать серверную библиотеку или решение вместо написания собственного кода для обработки учетных данных открытого ключа.
Затем вы можете сохранить информацию, полученную из учетных данных, в базе данных для будущего использования.
Следующий список содержит рекомендуемые для сохранения свойства:
- Идентификатор учетных данных : идентификатор учетных данных, возвращаемый вместе с учетными данными открытого ключа.
- Имя учетных данных : Имя учетных данных. Назовите его в честь поставщика ключа доступа, который его создал, и который можно идентифицировать на основе AAGUID .
- Идентификатор пользователя : идентификатор пользователя, использованный для создания ключа доступа.
- Открытый ключ : открытый ключ, возвращаемый с учетными данными открытого ключа. Это необходимо для проверки утверждения ключа доступа.
- Дата и время создания : Запишите дату и время создания ключа доступа. Это полезно для идентификации ключа доступа.
- Дата и время последнего использования : регистрирует последнюю дату и время, когда пользователь использовал ключ доступа для входа в систему. Это полезно для определения того, какой ключ доступа использовал (или не использовал) пользователь.
- AAGUID : уникальный идентификатор поставщика ключа доступа.
- Флаг права на резервное копирование : true, если устройство имеет право на синхронизацию ключей доступа. Эта информация помогает пользователям идентифицировать синхронизируемые ключи доступа и привязанные к устройству (не синхронизируемые) ключи доступа на странице управления ключами доступа.
Следуйте более подробным инструкциям на странице Регистрация ключа доступа на стороне сервера.
Сигнал в случае неудачной регистрации
Если регистрация ключа доступа не удалась, это может вызвать путаницу у пользователя. Если у поставщика ключа доступа есть ключ доступа, доступный пользователю, но связанный с ним открытый ключ не хранится на стороне сервера, попытки входа с использованием ключа доступа никогда не будут успешными, и устранить неполадки будет сложно. Обязательно сообщите об этом пользователю, если это так.
Чтобы предотвратить такую ситуацию, вы можете сообщить поставщику ключа доступа неизвестный ключ доступа с помощью API Signal . Вызывая PublicKeyCredential.signalUnknownCredential()
с идентификатором RP и идентификатором учетных данных, RP может сообщить поставщику ключа доступа, что указанные учетные данные были удалены или не существуют. Поставщик ключа доступа решает, как обрабатывать этот сигнал, но если поддерживается, ожидается, что связанный ключ доступа будет удален.
// Detect authentication failure due to lack of the credential
if (response.status === 404) {
// Feature detection
if (PublicKeyCredential.signalUnknownCredential) {
await PublicKeyCredential.signalUnknownCredential({
rpId: "example.com",
credentialId: "vI0qOggiE3OT01ZRWBYz5l4MEgU0c7PmAA" // base64url encoded credential ID
});
} else {
// Encourage the user to delete the passkey from the password manager nevertheless.
...
}
}
Чтобы узнать больше об API Signal, прочтите статью Обеспечение соответствия ключей доступа учетным данным на вашем сервере с помощью API Signal .
Отправить уведомление пользователю
Отправка уведомления (например, по электронной почте) при регистрации ключа доступа помогает пользователям обнаружить несанкционированный доступ к учетной записи. Если злоумышленник создает ключ доступа без ведома пользователя, этот ключ доступа остается доступным для дальнейшего злоупотребления, даже после изменения пароля. Уведомление предупреждает пользователя и помогает предотвратить это.
Контрольный список
- Проверьте пользователя (предпочтительно с помощью электронной почты или безопасного метода), прежде чем разрешить ему создать ключ доступа.
- Предотвратите создание дубликатов ключей доступа для одного и того же поставщика ключей доступа с помощью
excludeCredentials
. - Сохраните AAGUID для идентификации поставщика ключа доступа и присвоения имени учетным данным пользователя.
- Сообщите, если попытка зарегистрировать ключ доступа не удалась с помощью
PublicKeyCredential.signalUnknownCredential()
. - Отправьте пользователю уведомление после создания и регистрации ключа доступа для его учетной записи.
Ресурсы
- Регистрация ключа доступа на стороне сервера
- Документ Apple: Аутентификация пользователя через веб-сервис
- Документ Google: Вход без пароля с ключами доступа
Следующий шаг: войдите в систему, используя пароль через автозаполнение форм .