О веб-аутентификации

API веб-аутентификации (или сокращенно WebAuthn) — это спецификация, написанная и опубликованная W3C и Альянсом FIDO. Атаки с фишингом и паролями могут уйти в прошлое благодаря использованию Credential Manager API, лежащего в основе WebAuthn.

Веб-аутентификация предлагает лучшую альтернативу для защиты нашей конфиденциальной онлайн-информации и снижает влияние утечек данных. Криптография с открытым ключом существует уже полвека и является одним из основных столпов современной онлайн-безопасности.

В основе веб-аутентификации лежат три основные концепции:

  • Аппаратный модуль безопасности хранит закрытые ключи и выполняет криптографические операции для аутентификации;
  • пара ключей работает только для определенного источника, что предотвращает фишинг;
  • аутентификаторы предоставляют сертификат, который помогает серверам идентифицировать и подтверждать источник открытого ключа.

Что касается аутентификаторов, мы можем выделить две основные категории:

  • платформенные аутентификаторы (встроенные в устройство)
  • перемещаемые аутентификаторы (внешнее устройство, которое может подключаться к другим платформам)

Наиболее распространенным примером перемещаемых аутентификаторов является Yubikey, который существует уже несколько лет. С его помощью пользователи могли добавить дополнительный уровень безопасности к своим онлайн-аккаунтам, используя Yubikey в качестве второго фактора аутентификации.

Поскольку современные устройства включают модель аппаратной безопасности (TPM для ноутбуков/ПК), поддержка средства проверки подлинности платформы за последние пару лет улучшилась. Web Authentication API также усовершенствовался и предлагает хорошую интеграцию с основными браузерами благодаря усилиям FIDO Alliance и вкладу в спецификацию W3C.

Браузерная платформа и поддержка роуминга

По состоянию на октябрь 2021 года совместимость браузеров для платформы и перемещаемых аутентификаторов выглядит следующим образом:

Поддержка аутентификатора платформы

Поддержка перемещаемого аутентификатора

API диспетчера учетных данных

Чтобы использовать API веб-аутентификации, нам нужно быть знакомым с API диспетчера учетных данных, потому что все наши взаимодействия используют две функции из navigator.credentials.

Я рекомендую вам прочитать Руководство по веб-аутентификации. Он обеспечивает отличное и понятное объяснение шагов, связанных с веб-аутентификацией.

Регистрация учетных данных

Чтобы пройти аутентификацию на сервере с помощью WebAuthn, нам сначала нужно создать пару ключей и загрузить открытый ключ на сервер, и мы можем сделать это с помощью navigator.credentials.create().

Обычные реализации веб-аутентификации включают два этапа:

  1. Мы запрашиваем некоторые начальные данные с сервера (также известного как проверяющая сторона), которые мы используем в качестве входных данных для функции navigator.credentials.create().
  2. Мы отправляем учетные данные, сгенерированные navigator.credentials.create(), обратно на сервер для хранения нашего открытого ключа.

Аутентификация также использует два этапа, с той разницей, что на втором этапе мы больше не предоставляем открытый ключ, а включаем подпись запроса, полученного в первый запрос.

Функция navigator.credentials.create может содержать один из трех параметров: пароль, объединенные учетные данные или параметры создания учетных данных с открытым ключом. В нашем случае мы будем использовать параметры открытого ключа, и это будет выглядеть так:

const credential = await navigator.credentials.create({
    publicKey: publicKeyCredentialCreationOptions
});

Мы можем легко сказать, что navigator.credentials.create() возвращает промис, и что нам нужно предоставить некоторые параметры для publicKey. Вот пример того, как могут выглядеть эти параметры:

const publicKeyCredentialCreationOptions = {
    challenge: Uint8Array.from(
        randomStringFromServer, c => c.charCodeAt(0)),
    rp: {
        name: "Demo Service",
        id: "demo-service.com",
    },
    user: {
        id: Uint8Array.from(
            idStringFromServer, c => c.charCodeAt(0)),
        name: "MadMax",
        displayName: "Max Kooky",
    },
    pubKeyCredParams: [{alg: -7, type: "public-key"}],
    authenticatorSelection: {
        authenticatorAttachment: "cross-platform",
    },
    timeout: 60000,
    attestation: "direct"
};

Большинство полей, которые мы видим выше, мы получаем от сервера (Relying Party, rp в опциях). name и displayName могут быть необязательными или предоставляться на стороне клиента.

Некоторые поля выделяются, например challenge и user.id. Спецификация требует, чтобы эти поля были в двоичном формате, поскольку именно так работает реализация Credential Manager API. К сожалению, большинство реализаций REST-серверов отправляют данные через JSON, который не может хранить двоичные данные.

Чтобы обойти это ограничение JSON, мы берем String, преобразуем его в ArrayBuffer и создаем представление Uint8Array.

После успешного выполнения navigator.credentials.create() мы возвращаем PublicKeyCredential:

// PublicKeyCredential
{
    id: 'ADSUllKQmbqdGtpu4sjseh4cg2TxSvrbcHDTBsv4NSSX9...',
    rawId: ArrayBuffer(59),
    response: AuthenticatorAttestationResponse {
        clientDataJSON: ArrayBuffer(121),
        attestationObject: ArrayBuffer(306),
    },
    type: 'public-key'
}

Эти учетные данные содержат открытый ключ и некоторую другую информацию для проверки сервером регистрации. В некоторых полях этих учетных данных используется бинарное представление (ArrayBuffer). Это означает, что перед отправкой учетных данных обратно на сервер нам необходимо преобразовать содержимое во что-то, что может использовать JSON. пройти по.

Кодировка Base64url хорошо подходит для нашего случая, а также обеспечивает безопасность URL.

Мы можем реализовать преобразования base64url самостоятельно или использовать существующую библиотеку. В любом случае это добавляет сложности при работе с параметрами и учетными данными. Как я уже упоминал во введении, я рекомендую ознакомиться с Credential Manager API.

Веб-компоненты

На GitHub есть интересное обсуждение использования веб-аутентификации без JavaScriptw3c/webauthn/issues/1255. Встроенная поддержка HTML была бы отличной реализацией. Это, безусловно, поможет сделать WebAuthn вездесущим, но пока это обсуждение не перерастет в предложение, я думаю, что мы могли бы улучшить DX с помощью веб-компонентов.

В связи с этим вместе с коллегой мы решили ускорить внедрение WebAuthn, предоставив стартовый проект Spring Boot, основанный на реализации Yubico, и коллекцию веб-компонентов. , которые изначально совместимы со стартером Spring Boot. Все ресурсы представляют собой программное обеспечение с открытым исходным кодом, и мы будем рады получить ваши отзывы и комментарии.

Вы можете найти их обоих на GitHub, TechWebAuthn.

Используя веб-компоненты, разработчикам нужно только стилизовать компоненты в соответствии со своими потребностями или требованиями, сохраняя при этом гибкость для внесения изменений в свойства или настройки работы определенных вещей.

Стоит отметить, что спецификация Web Authentication указывает, что для доступа к Credential Manager API требуется взаимодействие с пользователем (например, нажатие кнопки или отправка формы). Поэтому все веб-компоненты, упомянутые ниже, включают формы и используют событие формы submit для выполнения этих требований.

Следующий список компонентов поддерживает все основные церемонии:

  • webauthn-registration — используется для создания новой учетной записи.
  • webauthn-login — используется для подключения к ранее зарегистрированной учетной записи.
  • webauthn-recovery — используется для восстановления доступа к учетной записи.
  • webauthn-enrollment-requester — используется для запроса доступа к существующей учетной записи с нового устройства.
  • webauthn-enrollment-provider — используется для подтверждения и предоставления доступа к новому устройству с уже зарегистрированного устройства.
  • webauthn-rtc-enrollment-requester — аналогично webauthn-enrollment-requester, но также использует WebRTC для отправки конфиденциальных данных между устройствами.
  • webauthn-rtc-enrollment-provider — аналогично webauthn-enrollment-provider, но также использует WebRTC для отправки конфиденциальных данных между устройствами.

Добавление новых устройств в вашу учетную запись

Хотя первые три церемонии довольно просты, добавление дополнительных устройств к вашей учетной записи объясняется в спецификации W3C только с помощью роумингового аутентификатора.

Некоторые пользователи могут не позволить себе мобильный аутентификатор или захотят носить его с собой повсюду. Для этих ситуаций мы придумали процесс регистрации, который требует, чтобы пользователь имел доступ только к ранее зарегистрированному устройству и устройству, которое он хочет добавить.

Существует также несколько способов обработки регистрации только с помощью двух упомянутых выше устройств, поэтому мы включили компоненты для двух методов:

Пользователь может перемещать/вводить/вставлять сгенерированный токен (используемый для привязки нового устройства) с ранее зарегистрированного устройства на новую машину, которую он хочет зарегистрировать;

webauthn-enrollment-providerwebauthn-enrollment-requester

Сгенерированные токены представляют собой большие случайные строки, закодированные в Base64, а это означает, что их нелегко набирать, и их перемещение по Интернету (WhatsApp, Gmail и другие) может быть не очень хорошей идеей.

Решением может быть использование проверяющей стороны (сервера) для облегчения связи между двумя устройствами, что мы также можем реализовать разными способами. Для этого хорошо подходят WebSockets. Например, мы могли бы подключить два устройства к комнате на основе кода, предоставленного еще незарегистрированным устройством, и они могли бы отправлять токен и любую другую информацию через этот канал WebSocket.

В зависимости от варианта использования службы, сохранение открытых многих каналов WebSocket может быть не идеальным. К счастью, в нашем распоряжении есть еще один веб-API, который может разгрузить это общение, — WebRTC API. Используя WebRTC, два устройства нуждаются только в представлении на этапе обнаружения, используя комнату WebSockets. После этого они могли выйти из соединения и продолжить общение в одноранговой сети.

webauthn-rtc-enrollment-providerwebauthn-rtc-enrollment-requester

Оба этих компонента включают код, необходимый для установления связи WebSocket и WebRTC.

Установка и использование

Все компоненты доступны как часть пакета webauthn-components на npm и могут быть установлены с помощью простой команды:

npm install webauthn-components

Все компоненты предоставляются в виде модулей ES и включают все необходимые кодировщики и декодеры по умолчанию.

Чтобы зарегистрировать новую учетную запись, мы импортируем необходимый компонент:

import "webauthn-components/registration";

html`<webauthn-registration></webauthn-registration>` // lit-html

или прямо в HTML

<script type="module" src="https://unpkg.com/webauthn-components/dist/webauthn-registration.js"></script>

<webauthn-registration></webauthn-registration>

Модуль также включает определение пользовательского элемента, что означает, что после импорта файла тег webauthn-registration готов к использованию.

Предположим, предоставленные значения по умолчанию не подходят для вашего проекта или варианта использования. В этом случае вы можете настроить почти все, так как компоненты предоставляют события, свойства для URL-адресов, функций и входных атрибутов, а также части CSS для каждого элемента внутри.

Например, вы можете зарегистрироваться без имени пользователя:

<webauthn-registration no-username></webauthn-registration>

или изменить текст кнопки:

<webauthn-registration button-text="Create new account"></webauthn-registration>

Мы также можем настроить более сложные свойства:

<webauthn-registration></webauthn-registration>

<script>
  const registration = document.querySelector('webauthn-registration');
  registration.fetchOptions.headers['Cache-Control'] = 'no-cache';
  registration.registrationStartUrl = '/api/v2/registration/start';
  registration.registerCredentialEncoder = (credential) => { /* Some transformation */ }
</script>

И мы можем легко стилизовать любой внутренний элемент, выбрав связанную с ним часть:

webauthn-registration::part(button) {
  border: none;
  background-color: #14a;
  color: #fcc;
  border-radius: 0.5em;
}

Более подробная документация по компонентам доступна в проекте README, также вы можете опробовать их на auth.marv.ro.

Никогда еще не было так легко начать работу с веб-аутентификацией, особенно с поддержкой платформ, охватывающей почти все комбинации поставщиков и систем, и все больше и больше платформ, стремящихся к беспарольной сети. W3C недавно опубликовал бесплатный курс под названием Введение в веб-аутентификацию на edX.

В следующий раз, когда вы будете создавать веб-приложение, подумайте о реализации WebAuthn и, возможно, опробуйте эти веб-компоненты. Они будут работать во всех основных браузерах и прекрасно сочетаются с любым фреймворком или библиотекой.

Ресурсы

Вы можете узнать больше о веб-аутентификации из ресурсов ниже: