エラーメッセージ
token is malformed: token contains an invalid number of segments
別の表記
- token is malformed: could not base64 decode header: illegal base64 data at input byte 6
- token contains an invalid number of segments
v5 は先頭に「token is malformed: 」を付けます。v4 は内側のテキストだけを出力します(例:「token contains an invalid number of segments」)。「illegal base64 data at input byte N」のバイトオフセットは、最初の不正な文字の位置を示します。
golang-jwt/jwt (Go) — jwt.ErrTokenMalformed
このエラーの意味
golang-jwt はトークンの構造を読み取ることすらできませんでした。「invalid number of segments」は、文字列に含まれるドットがちょうど 2 つではないことを意味します。「could not base64 decode header」は、最初のセグメントが base64url ではないことを意味し、典型的な原因は `Bearer ` プレフィックスが付いたままになっていることです(バイト位置 6 がスペースです)。v5 ではこれらが `jwt.ErrTokenMalformed` でラップされます。鍵の検索は行われていません。
発生する理由
Authorization ヘッダーの値全体を解析している
よくある`r.Header.Get("Authorization")` は `Bearer eyJ…` を返します。これをそのまま `jwt.Parse` に渡すと、スペースのせいでヘッダーのセグメントが base64 として不正になります。
値が JWT ではない
よくある不透明な OAuth アクセストークン、セッション ID、API キー、または空のヘッダーがパーサーに渡されています。base64url のセグメントが 3 つある文字列だけが JWS トークンです。
セグメントが 5 つある暗号化トークン(JWE)
ときどきトークンを暗号化する ID プロバイダーもあります。golang-jwt が扱えるのは署名付きトークンだけです。先に JWE を復号し(たとえば go-jose を使います)、入れ子になった JWS を解析してください。
トークンが途中で切れている
ときどきデータベースのカラム、Cookie のサイズ制限(4 KB)、ログの行のいずれかによってトークンが切れ、最後のセグメントやドットが失われています。
修正方法
- 1.
request パッケージでトークンを抽出する
`github.com/golang-jwt/jwt/v5/request` は、`Bearer` スキームを大文字と小文字を区別せずに取り除き、トークンがまったくない場合は `request.ErrNoTokenInRequest` を返します。
go import "github.com/golang-jwt/jwt/v5/request" token, err := request.ParseFromRequest(r, request.OAuth2Extractor, keyfunc, request.WithParser(jwt.NewParser(jwt.WithValidMethods([]string{"ES256"})))) if err != nil { http.Error(w, "unauthorized", http.StatusUnauthorized) return } - 2.
解析する前に形式を検証する
ドットの数を数えれば、JWS(2 つ)、JWE(4 つ)、それ以外の無意味な値を区別できます。処理できるトークンでないものには、すべて 401 を返してください。
go raw := strings.TrimSpace(strings.TrimPrefix(r.Header.Get("Authorization"), "Bearer ")) switch strings.Count(raw, ".") { case 2: // JWS: parse it case 4: // JWE: decrypt first (go-jose), golang-jwt does not handle encrypted tokens default: http.Error(w, "unauthorized", http.StatusUnauthorized) return }