Pular para o conteúdo
ecdsa.com
Python

py_webauthn

O py_webauthn, da Duo Labs, a biblioteca WebAuthn de referência do lado da parte confiável (relying party) para Python: assinaturas de asserção apenas em DER e contadores estritos, mas a verificação do usuário só é exigida se você pedir.
Licença
BSD-3-Clause
Versão verificada
3.0.1 ·
Código-fonte
github.com/duo-labs/py_webauthn
Documentação
duo-labs.github.io/py_webauthn/

Visão geral da compatibilidade

WebAuthn

O py_webauthn (o pacote webauthn no PyPI) gera opções de registro e de autenticação e verifica as respostas, incluindo todos os formatos de atestado, sobre o pyca/cryptography. O teste alimentou a biblioteca com o mesmo registro e as mesmas asserções de um autenticador de software usados com o SimpleWebAuthn, o go-webauthn e o WebAuthn4J, e é isso que torna visíveis as diferenças entre eles.

Os padrões de política são os mais permissivos dos quatro. generate_registration_options() não envia nenhum membro authenticatorSelection, então os navegadores recorrem à verificação do usuário preferred e a chaves residentes discouraged — chaves de acesso que não são detectáveis —, e verify_registration_response() e verify_authentication_response() aceitaram respostas sem a flag UV, a menos que se passe require_user_verification=True. Por isso, um login construído sobre os padrões aceita um toque em uma chave de segurança sem PIN. Defina a política explicitamente nos dois lados, como faz o exemplo.

As assinaturas de asserção precisam estar em ASN.1 DER: uma assinatura r‖s bruta de 64 bytes falhou com Could not verify authentication signature, uma mensagem que não menciona a codificação; o conversor DER ⇄ bruto mostra se um cliente de teste produziu a forma errada. Uma assinatura DER high-S foi verificada. Ed25519 (-8), ES256 (-7) e RS256 (-257) são oferecidos por padrão; não há entrada para ML-DSA, ao contrário do SimpleWebAuthn 14.

Os contadores são estritos: Response sign count of 3 was not greater than current count of 5, e o mesmo para 0 depois de 5, enquanto 0 depois de 0 — o que as chaves de acesso sincronizadas informam — foi verificado. Armazene o new_sign_count retornado após cada login bem-sucedido.

Decodifique a saída bruta do navegador com o inspetor de WebAuthn e faça experimentos com segurança no ambiente de testes de chaves de acesso.

Os fatos e as verificações por trás deles

Nada abaixo foi copiado da documentação. A verificação content/libraries/verify/py-webauthn/run.sh executou py_webauthn 3.0.1 na imagem de contêiner python:3.14.8-slim-trixie em 20 de outubro de 2026; a página precisa corresponder ao que a verificação imprimiu, e cada fato lista essas linhas.

  • Algoritmos COSE padrão

    -8-7-257

    generate_registration_options() lista os algoritmos COSE -8 (Ed25519), -7 (ES256) e -257 (RS256).

    Registrado pela verificação
    fact webauthn-default-algorithms = -8, -7, -257
  • Atestado padrão

    none

    A preferência de transmissão do atestado (attestation conveyance) é none por padrão.

    Registrado pela verificação
    fact webauthn-attestation = none
  • Nível padrão de verificação do usuário

    preferred

    Por padrão, nenhum authenticatorSelection é enviado, então o navegador aplica o padrão do WebAuthn, preferred.

    Registrado pela verificação
    fact webauthn-user-verification = preferred
    evidence webauthn-user-verification: authenticatorSelection in the generated options: absent
  • Configuração padrão de chave residente

    discouraged

    Sem authenticatorSelection, vale o padrão do WebAuthn, discouraged, então os autenticadores não são solicitados a criar credenciais detectáveis.

    Registrado pela verificação
    fact webauthn-resident-key = discouraged
  • Verificação do usuário exigida por padrão

    no

    Um registro e uma asserção só com a flag UP foram verificados; com require_user_verification=True, a asserção falhou com User verification is required but user was not verified during authentication.

    Registrado pela verificação
    fact webauthn-uv-enforced-by-default = no
    evidence webauthn-uv-enforced-by-default: registration with the UV flag clear: verified
  • Codificação da assinatura de asserção

    der

    Uma asserção ES256 correta com assinatura DER foi verificada.

    Registrado pela verificação
    fact webauthn-assertion-encoding = der
  • Assinatura de asserção r‖s bruta

    rejected

    Uma assinatura r‖s bruta de 64 bytes falhou com InvalidAuthenticationResponse: Could not verify authentication signature.

    Registrado pela verificação
    fact webauthn-raw-signature = rejected
  • Assinatura de asserção high-S

    accepted

    Uma assinatura DER com s substituído por n − s foi verificada.

    Registrado pela verificação
    fact webauthn-high-s = accepted
  • Contador de assinaturas que retrocede

    rejected

    Os contadores 3 e 0 depois de um 5 armazenado falharam com Response sign count of … was not greater than current count of 5; 0 depois de um 0 armazenado foi verificado.

    Registrado pela verificação
    fact webauthn-counter-regression = rejected
  • Credenciais Ed25519

    supported

    Uma credencial Ed25519 (COSE -8) foi registrada e autenticada com sucesso.

    Registrado pela verificação
    fact webauthn-ed25519 = supported
    evidence webauthn-ed25519: verified

Uso

Gere opções de registro e de autenticação que exijam a verificação do usuário e uma credencial detectável, em vez dos padrões da biblioteca.
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"])

O executor de trechos de código roda cada exemplo literalmente na imagem de contêiner python:3.14.8-slim-trixie, e a execução falha se a saída não corresponder.

Configuração
pip install --quiet --root-user-action=ignore webauthn==3.0.1
Comando
python usage.py
A saída deve corresponder a (expressão regular)
uv: required required\nresident key: required

Bibliotecas