|
8 | 8 | "strings"
|
9 | 9 | )
|
10 | 10 |
|
| 11 | +const tokenDelimiter = "." |
| 12 | + |
11 | 13 | type Parser struct {
|
12 | 14 | // If populated, only these methods will be considered valid.
|
13 | 15 | validMethods []string
|
@@ -136,9 +138,10 @@ func (p *Parser) ParseWithClaims(tokenString string, claims Claims, keyFunc Keyf
|
136 | 138 | // It's only ever useful in cases where you know the signature is valid (since it has already
|
137 | 139 | // been or will be checked elsewhere in the stack) and you want to extract values from it.
|
138 | 140 | func (p *Parser) ParseUnverified(tokenString string, claims Claims) (token *Token, parts []string, err error) {
|
139 |
| - parts = strings.Split(tokenString, ".") |
140 |
| - if len(parts) != 3 { |
141 |
| - return nil, parts, newError("token contains an invalid number of segments", ErrTokenMalformed) |
| 141 | + var ok bool |
| 142 | + parts, ok = splitToken(tokenString) |
| 143 | + if !ok { |
| 144 | + return nil, nil, newError("token contains an invalid number of segments", ErrTokenMalformed) |
142 | 145 | }
|
143 | 146 |
|
144 | 147 | token = &Token{Raw: tokenString}
|
@@ -196,6 +199,33 @@ func (p *Parser) ParseUnverified(tokenString string, claims Claims) (token *Toke
|
196 | 199 | return token, parts, nil
|
197 | 200 | }
|
198 | 201 |
|
| 202 | +// splitToken splits a token string into three parts: header, claims, and signature. It will only |
| 203 | +// return true if the token contains exactly two delimiters and three parts. In all other cases, it |
| 204 | +// will return nil parts and false. |
| 205 | +func splitToken(token string) ([]string, bool) { |
| 206 | + parts := make([]string, 3) |
| 207 | + header, remain, ok := strings.Cut(token, tokenDelimiter) |
| 208 | + if !ok { |
| 209 | + return nil, false |
| 210 | + } |
| 211 | + parts[0] = header |
| 212 | + claims, remain, ok := strings.Cut(remain, tokenDelimiter) |
| 213 | + if !ok { |
| 214 | + return nil, false |
| 215 | + } |
| 216 | + parts[1] = claims |
| 217 | + // One more cut to ensure the signature is the last part of the token and there are no more |
| 218 | + // delimiters. This avoids an issue where malicious input could contain additional delimiters |
| 219 | + // causing unecessary overhead parsing tokens. |
| 220 | + signature, _, unexpected := strings.Cut(remain, tokenDelimiter) |
| 221 | + if unexpected { |
| 222 | + return nil, false |
| 223 | + } |
| 224 | + parts[2] = signature |
| 225 | + |
| 226 | + return parts, true |
| 227 | +} |
| 228 | + |
199 | 229 | // DecodeSegment decodes a JWT specific base64url encoding. This function will
|
200 | 230 | // take into account whether the [Parser] is configured with additional options,
|
201 | 231 | // such as [WithStrictDecoding] or [WithPaddingAllowed].
|
|
0 commit comments