fish2018/pansou · error
unexpected signing method
Error message
unexpected signing method
What it means
During jwt.ParseWithClaims the keyfunc checks that the token's alg header is an HMAC signing method; anything else (e.g. RS256, none) is rejected with "unexpected signing method". This prevents algorithm-confusion attacks where a token signed/unsigned with another alg is accepted.
Solutions
- Ensure the token was signed with the same HMAC algorithm (HS256) and secret as this validator
- Inspect the token header (base64-decode the first segment) to see its alg value
- If you must support other algorithms, extend the keyfunc to handle them explicitly — never accept alg=none
- Regenerate tokens with the correct signing method after fixing the issuer
Example fix
// before
if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok {
return nil, errors.New("unexpected signing method")
}
// after (tighter)
if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok || token.Header["alg"] != "HS256" {
return nil, fmt.Errorf("unexpected signing method: %v", token.Header["alg"])
} Defensive patterns
Strategy: try-catch
Validate before calling
// decode token header client-side seg := strings.Split(tokenString, ".")[0] hdr, _ := base64.RawURLEncoding.DecodeString(seg) // verify alg is HS256 before sending
Try / catch
claims, err := util.ValidateToken(tokenString, secret)
if err != nil && strings.Contains(err.Error(), "unexpected signing method") {
// token from wrong issuer/algorithm: reject as untrusted
} Prevention
- Sign all tokens with HS256 and the shared secret
- Never accept alg=none or tokens from other algorithm families
- Pin the expected algorithm in the keyfunc
When it happens
Trigger: Presenting a JWT whose header declares a non-HMAC alg to ValidateToken; a forged token with alg=none; tokens issued by a service using RS/ES algorithms being validated by this HMAC-only code.
Common situations: Migrating between JWT libraries or services with different signing algorithms; an attacker crafting alg=none tokens; copy-pasted tokens from another project.
Understand the failure class
Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.
Related errors
AI-assisted analysis of fish2018/pansou@beaa561337 (2026-09-07).
Data as JSON: /api/errors/4483d5801ec69ef0.
Report an issue: GitHub.
Appendix: source
Thrown at util/jwt.go:53
token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
return token.SignedString([]byte(secret))
}
// ValidateToken 验证JWT token
func ValidateToken(tokenString string, secret string) (*Claims, error) {
if tokenString == "" {
return nil, errors.New("token cannot be empty")
}
if secret == "" {
return nil, errors.New("secret cannot be empty")
}
claims := &Claims{}
token, err := jwt.ParseWithClaims(tokenString, claims, func(token *jwt.Token) (interface{}, error) {
// 验证签名算法
if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok {
return nil, errors.New("unexpected signing method")
}
return []byte(secret), nil
})
if err != nil {
return nil, err
}
if !token.Valid {
return nil, errors.New("invalid token")
}
return claims, nil
}
View on GitHub (pinned to beaa561337)