错误信息
token signature is invalid: signing method ES256 is invalid
也可能输出为
- signing method ES256 is invalid
- token signature is invalid: signing method HS256 is invalid
- token is unverifiable: signing method (alg) is unavailable
v4 打印的是“signing method ES256 is invalid”。如果 `alg` 的值是 golang-jwt 完全不认识的(或者是没有使用专用常量的 `none`),得到的则是“token is unverifiable: signing method (alg) is unavailable”。
golang-jwt/jwt (Go) — WithValidMethods rejected the token's alg
含义
令牌的 `alg` 头部不在你通过 `jwt.WithValidMethods` 传入的列表中,因此 golang-jwt 在调用你的 Keyfunc 获取密钥之前就拒绝了它。之所以有这项检查,是因为头部由攻击者控制:决定接受哪种算法的是验证方,而不是令牌。v5 把它归入 `ErrTokenSignatureInvalid`,因此看起来像是签名有误——实际上这是算法策略导致的拒绝。
原因
颁发者更换了算法
常见身份提供商会从 RS256 迁移到 ES256,或者新增 EdDSA 密钥。固定使用旧算法的验证方,从切换那一刻起会拒绝所有新令牌。
允许列表是从另一个服务复制过来的
偶见为某个颁发者(RS256)配置的共享中间件,被复用到了另一个使用 ES256 签名的颁发者上。允许列表必须对应当前这个验证方背后的颁发者。
来自别处的令牌,或者是探测
偶见到达 RS256/ES256 API 的 `alg: HS256` 或 `none` 令牌,是典型的算法混淆探测。应继续拒绝这类令牌。
修复方法
- 1.
只允许颁发者实际使用的算法
读取颁发者的 JWKS(每个密钥的 `alg`)或发现文档(`id_token_signing_alg_values_supported`),并固定使用这些值。迁移期间同时列出新旧两种算法,并按 `kid` 选择密钥。
go token, err := jwt.Parse(raw, keyfunc, jwt.WithValidMethods([]string{"ES256"}), // what the issuer signs with today jwt.WithIssuer("https://issuer.example.com/"), ) - 2.
在 Keyfunc 中断言签名方法的类型
在 Keyfunc 中检查 `t.Method` 的具体类型,可以保证 ECDSA 密钥只会与 ECDSA 算法一起使用,即使以后放宽了允许列表也是如此。
go keyfunc := func(t *jwt.Token) (any, error) { // Belt and braces: the key type must fit the algorithm family. if _, ok := t.Method.(*jwt.SigningMethodECDSA); !ok { return nil, fmt.Errorf("unexpected signing method %v", t.Header["alg"]) } return lookupKey(t.Header["kid"]) }