跳到主要内容
ecdsa.com
Go

go-webauthn

go-webauthn,Go 的 WebAuthn 依赖方库:断言签名只接受 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,或面向 net/http 以外框架的 Validate* 变体——并能验证所有证明格式。在检查中,它校验了与 SimpleWebAuthn、py_webauthn 和 WebAuthn4J 所用相同的软件认证器响应。

策略来自配置,并通过 SessionData 传递。AuthenticatorSelection 为空时,BeginRegistration 发送的是空对象,于是浏览器采用默认值——用户验证为 preferred,驻留密钥为 discouraged——而校验时接受了未设置 UV 标志的响应,因为会话只要求 preferred。会话中设置 UserVerification: required 后,同一个断言报错 User verification required but flag not set by authenticator。应像示例那样在 Config.AuthenticatorSelection 中设置策略,并在两次调用之间保存完整的 SessionData。

计数器的处理是它与其他库最重要的区别。计数器(3)低于已存储值(5)的断言照样登录成功:返回的凭证设置了 Authenticator.CloneWarning,SignCount 仍保持为 5。该库只报告可能存在克隆;是否拒绝登录由你的代码决定,而代码必须在每次登录后检查 CloneWarning。

断言签名必须是 ASN.1 DER——64 字节的原始 r‖s 签名报错 Error validating the assertion signature: Signature invalid or not provided——而 high-S 的 DER 签名验证通过。默认的 pubKeyCredParams 列表很长:ES256、ES384 和 ES512(-7、-35、-36),RS256 到 RS512(-257 到 -259),PS256 到 PS512(-37 到 -39),以及 Ed25519(-8);Ed25519 凭证完成了端到端验证。0.18.2 中没有 ML-DSA 条目。

用 WebAuthn 查看器解码浏览器发送的数据,用 AAGUID 查询查找认证器型号,并在通行密钥沙盒中试用各个流程。

事实及其依据的检查

以下内容均非抄自文档。检查脚本 content/libraries/verify/go-webauthn/run.sh 于2026年10月20日在容器镜像 golang:1.27.3-trixie 中运行了 go-webauthn 0.18.2;本页内容必须与它的输出一致,每条事实都列出了对应的输出行。

  • 默认 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 时,只设置了 UP 标志的注册和断言都验证通过;设为 required 时,断言报错 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
  • 断言签名编码

    der

    带 DER 签名的正确 ES256 断言验证通过。

    检查记录
    fact webauthn-assertion-encoding = der
  • 原始 r‖s 断言签名

    rejected

    64 字节的原始 r‖s 签名报错 Error validating the assertion signature: Signature invalid or not provided。

    检查记录
    fact webauthn-raw-signature = rejected
  • high-S 断言签名

    accepted

    把 s 替换为 n − s 的 DER 签名验证通过。

    检查记录
    fact webauthn-high-s = accepted
  • 签名计数器回退

    flagged

    已存储值为 5 时,计数器为 3 的登录并未失败:返回的凭证中 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

库