本文へスキップ
ecdsa.com
Python

py_webauthn

Duo Labs の py_webauthn は、Python 向けの WebAuthn リライングパーティライブラリの定番です。アサーション署名は DER のみを受け付け、カウンターのチェックも厳格ですが、ユーザー検証は明示的に求めない限り強制されません。
ライセンス
BSD-3-Clause
確認したバージョン
3.0.1 ·
ソースコード
github.com/duo-labs/py_webauthn
ドキュメント
duo-labs.github.io/py_webauthn/

互換性の概要

WebAuthn

py_webauthn(PyPI の webauthn パッケージ)は、pyca/cryptography の上で、登録用と認証用のオプションを生成し、すべてのアテステーション形式に対応してレスポンスを検証します。チェックでは、SimpleWebAuthn、go-webauthn、WebAuthn4J で使ったものと同じソフトウェア認証器の登録とアサーションを渡しました。そのため、ライブラリ間の違いがはっきりわかります。

ポリシーのデフォルトは 4 つのライブラリの中で最も緩くなっています。generate_registration_options() は authenticatorSelection メンバーをまったく送信しないため、ブラウザーはユーザー検証を preferred、レジデントキーを discouraged(検出可能ではないパスキー)とするデフォルトにフォールバックします。また、require_user_verification=True を渡さない限り、verify_registration_response() と verify_authentication_response() は UV フラグが立っていないレスポンスも受け入れました。そのため、デフォルトのまま構築したログインは、PIN なしでセキュリティキーをタップしただけで通ってしまいます。サンプルのように、両側でポリシーを明示的に設定してください。

アサーション署名は ASN.1 DER でなければなりません。64 バイトの生の r‖s 署名は Could not verify authentication signature で失敗しましたが、このメッセージはエンコーディングに触れていません。テスト用クライアントが誤った形式を生成していないかは、DER ⇄ 生形式の変換ツールで確認できます。high-S の DER 署名は検証を通りました。デフォルトでは Ed25519(-8)、ES256(-7)、RS256(-257)が提示され、SimpleWebAuthn 14 と異なり ML-DSA の項目はありません。

カウンターのチェックは厳格です。保存済みの値が 5 のときにカウンター 3 を送ると Response sign count of 3 was not greater than current count of 5 で失敗し、5 のあとの 0 も同様に失敗しましたが、0 のあとの 0(同期パスキーが報告する値)は検証を通りました。ログインに成功するたびに、返された new_sign_count を保存してください。

ブラウザーの生の出力は WebAuthn インスペクターでデコードし、パスキーサンドボックスで安全に試してください。

事実とその根拠となるチェック

以下の内容はドキュメントからの転記ではありません。2026年10月20日に、チェック content/libraries/verify/py-webauthn/run.sh がコンテナイメージ python:3.14.8-slim-trixie 内で py_webauthn 3.0.1 を実行しました。このページの内容はその出力と一致していなければならず、各事実には該当する出力行を示しています。

  • デフォルトの 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

    UP だけが立った登録とアサーションは検証を通りました。require_user_verification=True を指定すると、アサーションは 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
  • アサーション署名のエンコーディング

    der

    DER 署名を含む正しい ES256 アサーションは検証を通りました。

    チェックによる記録
    fact webauthn-assertion-encoding = der
  • 生の r‖s 形式のアサーション署名

    rejected

    64 バイトの生の r‖s 署名は InvalidAuthenticationResponse: Could not verify authentication signature で失敗しました。

    チェックによる記録
    fact webauthn-raw-signature = rejected
  • high-S のアサーション署名

    accepted

    s を n − s に置き換えた DER 署名も検証を通りました。

    チェックによる記録
    fact webauthn-high-s = accepted
  • 署名カウンターの巻き戻り

    rejected

    保存済みの値が 5 のとき、カウンター 3 と 0 は 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

ライブラリ