From 6ddbf9f86f58b84e9f2fd669f95bc82c035250c9 Mon Sep 17 00:00:00 2001 From: Mateus Caruccio Date: Mon, 17 Nov 2025 03:45:37 +0000 Subject: [PATCH] Fix CVE-2025-30204/GHSA-mh63-6h87-95cp Backport of https://github.com/golang-jwt/jwt/commit/0951d184286dece21f73c85673fd308786ffe9c3 --- parser.go | 38 ++++++++++++++++++++++++++++++++++---- 1 file changed, 34 insertions(+), 4 deletions(-) diff --git a/parser.go b/parser.go index d6901d9a..ea82e712 100644 --- a/parser.go +++ b/parser.go @@ -7,6 +7,9 @@ import ( "strings" ) +// backport https://github.com/golang-jwt/jwt/commit/0951d184286dece21f73c85673fd308786ffe9c3 +const tokenDelimiter = "." + type Parser struct { ValidMethods []string // If populated, only these methods will be considered valid UseJSONNumber bool // Use JSON Number format in JSON decoder @@ -94,11 +97,11 @@ func (p *Parser) ParseWithClaims(tokenString string, claims Claims, keyFunc Keyf // been checked previously in the stack) and you want to extract values from // it. func (p *Parser) ParseUnverified(tokenString string, claims Claims) (token *Token, parts []string, err error) { - parts = strings.Split(tokenString, ".") - if len(parts) != 3 { - return nil, parts, NewValidationError("token contains an invalid number of segments", ValidationErrorMalformed) + var ok bool + parts, ok = splitToken(tokenString) + if !ok { + return nil, nil, NewValidationError("token contains an invalid number of segments", ValidationErrorMalformed) } - token = &Token{Raw: tokenString} // parse Header @@ -146,3 +149,30 @@ func (p *Parser) ParseUnverified(tokenString string, claims Claims) (token *Toke return token, parts, nil } + +// splitToken splits a token string into three parts: header, claims, and signature. It will only +// return true if the token contains exactly two delimiters and three parts. In all other cases, it +// will return nil parts and false. +func splitToken(token string) ([]string, bool) { + parts := make([]string, 3) + header, remain, ok := strings.Cut(token, tokenDelimiter) + if !ok { + return nil, false + } + parts[0] = header + claims, remain, ok := strings.Cut(remain, tokenDelimiter) + if !ok { + return nil, false + } + parts[1] = claims + // One more cut to ensure the signature is the last part of the token and there are no more + // delimiters. This avoids an issue where malicious input could contain additional delimiters + // causing unecessary overhead parsing tokens. + signature, _, unexpected := strings.Cut(remain, tokenDelimiter) + if unexpected { + return nil, false + } + parts[2] = signature + + return parts, true +}