Перейти к содержимому
ecdsa.com
Python

py_webauthn

py_webauthn от Duo Labs — эталонная библиотека доверяющей стороны WebAuthn для Python: подписи assertion только в DER и строгие счётчики, но проверку пользователя библиотека требует, только если явно это указать.
Лицензия
BSD-3-Clause
Проверенная версия
3.0.1 ·
Исходный код
github.com/duo-labs/py_webauthn
Документация
duo-labs.github.io/py_webauthn/

Совместимость вкратце

WebAuthn

Библиотека py_webauthn (пакет webauthn в PyPI) построена поверх pyca/cryptography: она формирует параметры регистрации и аутентификации и проверяет ответы, включая все форматы аттестации. В ходе проверки ей передавали ту же регистрацию и те же assertion программного аутентификатора, что и SimpleWebAuthn, go-webauthn и WebAuthn4J, поэтому различия между библиотеками хорошо видны.

Политика по умолчанию здесь мягче, чем у остальных трёх библиотек. generate_registration_options() вообще не отправляет поле authenticatorSelection, поэтому браузеры применяют значения по умолчанию: проверка пользователя preferred и резидентный ключ discouraged (то есть ключи доступа создаются необнаруживаемыми), а verify_registration_response() и verify_authentication_response() принимали ответы со сброшенным флагом UV, если не передать require_user_verification=True. Поэтому вход, построенный на настройках по умолчанию, принимает касание ключа безопасности без ввода PIN. Задайте политику явно с обеих сторон — в параметрах и при проверке, как в примере.

Подписи assertion должны быть в ASN.1 DER: подпись в формате raw r‖s длиной 64 байта отклонена с ошибкой Could not verify authentication signature, в которой кодировка не упоминается; конвертер DER ⇄ raw покажет, не выдал ли тестовый клиент подпись не в той форме. Подпись DER с high-S прошла проверку. По умолчанию предлагаются Ed25519 (-8), ES256 (-7) и RS256 (-257); записи для ML-DSA нет, в отличие от SimpleWebAuthn 14.

Счётчики проверяются строго: Response sign count of 3 was not greater than current count of 5, и то же самое для 0 после 5, а значение 0 после 0 — именно его сообщают синхронизируемые ключи доступа — прошло проверку. Сохраняйте возвращённый new_sign_count после каждого успешного входа.

Декодируйте необработанные данные от браузера в инспекторе WebAuthn и безопасно экспериментируйте в песочнице ключей доступа.

Факты и проверки, на которых они основаны

Ничего из приведённого ниже не скопировано из документации. 20 октября 2026 г. проверка content/libraries/verify/py-webauthn/run.sh запустила py_webauthn 3.0.1 в образе контейнера python:3.14.8-slim-trixie; содержимое страницы должно совпадать с выводом проверки, а у каждого факта приведены соответствующие строки вывода.

  • Алгоритмы COSE по умолчанию

    -8-7-257

    generate_registration_options() перечисляет алгоритмы COSE -8 (Ed25519), -7 (ES256) и -257 (RS256).

    Что записала проверка
    fact webauthn-default-algorithms = -8, -7, -257
  • Аттестация по умолчанию

    none

    Параметр передачи аттестации по умолчанию равен none.

    Что записала проверка
    fact webauthn-attestation = none
  • Проверка пользователя по умолчанию

    preferred

    По умолчанию authenticatorSelection не отправляется, поэтому браузер применяет значение WebAuthn по умолчанию — preferred.

    Что записала проверка
    fact webauthn-user-verification = preferred
    evidence webauthn-user-verification: authenticatorSelection in the generated options: absent
  • Резидентный ключ по умолчанию

    discouraged

    Без authenticatorSelection действует значение WebAuthn по умолчанию — discouraged, поэтому аутентификаторы не получают запроса на создание обнаруживаемых учётных данных.

    Что записала проверка
    fact webauthn-resident-key = discouraged
  • Обязательная проверка пользователя по умолчанию

    no

    Регистрация и assertion, в которых установлен только флаг UP, прошли проверку; с require_user_verification=True assertion отклонён с ошибкой User verification is required but user was not verified during authentication.

    Что записала проверка
    fact webauthn-uv-enforced-by-default = no
    evidence webauthn-uv-enforced-by-default: registration with the UV flag clear: verified
  • Кодировка подписи assertion

    der

    Проверка корректного assertion ES256 с подписью в DER прошла успешно.

    Что записала проверка
    fact webauthn-assertion-encoding = der
  • Подпись assertion в формате raw r‖s

    rejected

    Подпись в формате raw r‖s длиной 64 байта отклонена с ошибкой InvalidAuthenticationResponse: Could not verify authentication signature.

    Что записала проверка
    fact webauthn-raw-signature = rejected
  • Подпись assertion с high-S

    accepted

    Подпись в DER, в которой s заменили на n − s, прошла проверку.

    Что записала проверка
    fact webauthn-high-s = accepted
  • Уменьшение счётчика подписей

    rejected

    Значения счётчика 3 и 0 при сохранённом 5 отклонены с ошибкой Response sign count of … was not greater than current count of 5; 0 при сохранённом 0 прошло проверку.

    Что записала проверка
    fact webauthn-counter-regression = rejected
  • Учётные данные Ed25519

    supported

    Учётные данные Ed25519 (COSE -8) успешно прошли регистрацию и аутентификацию.

    Что записала проверка
    fact webauthn-ed25519 = supported
    evidence webauthn-ed25519: verified

Использование

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

from webauthn import generate_authentication_options, generate_registration_options, options_to_json
from webauthn.helpers.structs import AuthenticatorSelectionCriteria, ResidentKeyRequirement, UserVerificationRequirement

# The defaults send no authenticatorSelection at all, and verification does not
# require UV. State the policy in the options, and pass
# require_user_verification=True to verify_registration_response() and
# verify_authentication_response() so the server enforces the same policy.
registration = generate_registration_options(
    rp_id="example.com",
    rp_name="Example",
    user_name="user@example.com",
    authenticator_selection=AuthenticatorSelectionCriteria(
        resident_key=ResidentKeyRequirement.REQUIRED,
        user_verification=UserVerificationRequirement.REQUIRED,
    ),
)
authentication = generate_authentication_options(rp_id="example.com", user_verification=UserVerificationRequirement.REQUIRED)

selection = json.loads(options_to_json(registration))["authenticatorSelection"]
print("uv:", selection["userVerification"], json.loads(options_to_json(authentication))["userVerification"])
print("resident key:", selection["residentKey"])

Тестовый стенд для фрагментов кода запускает каждый пример без изменений в образе контейнера python:3.14.8-slim-trixie, и запуск завершается ошибкой, если вывод не совпадает.

Подготовка
pip install --quiet --root-user-action=ignore webauthn==3.0.1
Команда
python usage.py
Вывод должен совпадать (регулярное выражение)
uv: required required\nresident key: required

Библиотеки