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

go-webauthn

go-webauthn — библиотека доверяющей стороны WebAuthn для Go: подписи assertion только в DER, проверка пользователя, которой управляют данные сессии, и откат счётчика, о котором библиотека сообщает предупреждением о клонировании, а не ошибкой входа.
Лицензия
BSD-3-Clause
Проверенная версия
0.18.2 ·
Исходный код
github.com/go-webauthn/webauthn
Документация
pkg.go.dev/github.com/go-webauthn/webauthn/webauthn

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

WebAuthn

github.com/go-webauthn/webauthn (поддерживаемый форк duo-labs/webauthn) проводит обе церемонии — BeginRegistration/FinishRegistration и BeginLogin/FinishLogin или их варианты Validate* для фреймворков помимо net/http — и проверяет все форматы аттестации. Проверка проводилась на тех же ответах программного аутентификатора, что и для SimpleWebAuthn, py_webauthn и WebAuthn4J.

Политика берётся из конфигурации и передаётся в SessionData. С пустым AuthenticatorSelection вызов BeginRegistration отправил пустой объект, поэтому браузеры применяют значения по умолчанию (проверка пользователя preferred, резидентный ключ discouraged), а при проверке принимались ответы со сброшенным флагом UV, потому что сессия требовала только preferred. Когда в сессии было задано UserVerification: required, тот же assertion отклонялся с ошибкой User verification required but flag not set by authenticator. Задайте политику в Config.AuthenticatorSelection, как в примере, и сохраняйте весь SessionData между двумя вызовами.

Обработка счётчика — самое важное отличие от других библиотек. Вход с assertion, у которого счётчик (3) меньше сохранённого (5), прошёл успешно: у возвращённых учётных данных был установлен Authenticator.CloneWarning, а SignCount остался равен 5. Библиотека лишь сообщает о возможном клоне; отказывать ли во входе, решает ваш код, и он должен проверять CloneWarning после каждого входа.

Подписи assertion должны быть в ASN.1 DER: подпись в формате raw r‖s длиной 64 байта отклонена с ошибкой Error validating the assertion signature: Signature invalid or not provided, а подпись DER с high-S прошла проверку. Список pubKeyCredParams по умолчанию длинный: ES256, ES384 и ES512 (-7, -35, -36), от RS256 до RS512 (от -257 до -259), от PS256 до PS512 (от -37 до -39) и Ed25519 (-8); учётные данные Ed25519 прошли сквозную проверку. Записи для ML-DSA в версии 0.18.2 нет.

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

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

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

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

    -7-35-36-257-258-259-37-38-39-8

    BeginRegistration перечисляет алгоритмы COSE -7, -35, -36 (ES256/384/512), -257, -258, -259 (RS256/384/512), -37, -38, -39 (PS256/384/512) и -8 (Ed25519).

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

    none

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

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

    preferred

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

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

    discouraged

    Если в параметрах нет residentKey, действует значение WebAuthn по умолчанию — discouraged.

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

    no

    Когда в сессии для проверки пользователя задано preferred, регистрация и assertion, в которых установлен только флаг UP, прошли проверку; с required assertion отклонён с ошибкой User verification required but flag not set by authenticator.

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

    der

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

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

    rejected

    Подпись в формате raw r‖s длиной 64 байта отклонена с ошибкой Error validating the assertion signature: Signature invalid or not provided.

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

    accepted

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

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

    flagged

    Значение счётчика 3 при сохранённом 5 не привело к ошибке входа: у возвращённых учётных данных CloneWarning равен true, а SignCount по-прежнему равен 5.

    Что записала проверка
    fact webauthn-counter-regression = flagged
    evidence webauthn-counter-regression: counter 3 after a stored 5: login succeeded, CloneWarning true, SignCount now 5
    evidence webauthn-counter-regression: counter 0 after a stored 0: verified, CloneWarning false
  • Учётные данные Ed25519

    supported

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

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

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

Настройте доверяющую сторону так, чтобы она требовала проверку пользователя и обнаруживаемые учётные данные, и посмотрите, как политика отражается в параметрах и в сессии.
package main

import (
	"fmt"

	"github.com/go-webauthn/webauthn/protocol"
	"github.com/go-webauthn/webauthn/webauthn"
)

type user struct{}

func (user) WebAuthnID() []byte                         { return []byte("user-1") }
func (user) WebAuthnName() string                       { return "user@example.com" }
func (user) WebAuthnDisplayName() string                { return "User" }
func (user) WebAuthnCredentials() []webauthn.Credential { return nil }

func main() {
	w, _ := webauthn.New(&webauthn.Config{
		RPID: "example.com", RPDisplayName: "Example", RPOrigins: []string{"https://example.com"},
		// Without this, options say nothing about UV or discoverable credentials.
		AuthenticatorSelection: protocol.AuthenticatorSelection{
			ResidentKey: protocol.ResidentKeyRequirementRequired, UserVerification: protocol.VerificationRequired,
		},
	})
	creation, session, _ := w.BeginRegistration(user{})
	// The session carries the requirement into FinishRegistration/FinishLogin.
	fmt.Println("uv:", creation.Response.AuthenticatorSelection.UserVerification, session.UserVerification)
	fmt.Println("resident key:", creation.Response.AuthenticatorSelection.ResidentKey)
}

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

Команда
GOFLAGS=-mod=mod go run .
Вывод должен совпадать (регулярное выражение)
uv: required required\nresident key: required

Библиотеки