516 文字
3 分

Q. JWTから渡されたBase64文字列をwindow.atob()に渡すとエラーが出る場合がある

2024-01-04

JWT をクライアントでデコードする際に、エラーが出る場合があると相談を受けた。

window.btoa()などにはUnicode 問題があることが知られており、元の文字列に日本語が含まれているケースでのみ発生していたため、それが原因であると考えたが、文字列によっては日本語でもエラーが出ないケースがあったため、 追加で調査を行った。

Base64 の変換方法について#

Base64 はバイナリーからテキストへの符号化を行う手法であり、転送中にバイナリデータが変換されていないことを保証できる。次のような変換を行う。

const uint8arr = Array.from(new TextEncoder().encode("ABCDEFG"));
// (7) [65, 66, 67, 68, 69, 70, 71]
const b8 = uint8arr.map((v) => v.toString(2).padStart(8, "0"));
// (7) ['01000001', '01000010', '01000011', '01000100', '01000101', '01000110', '01000111']
const b6 = b8
.join("")
.padEnd(Math.ceil(b8.length / 6) * 6, "0")
.match(/.{6}/g);
// (10) ['010000', '010100', '001001', '000011', '010001', '000100', '010101', '000110', '010001', '110000']
// 変換表に従って変換する
const t = convert(b6).map((v) => v.padEnd(4, "="));
// ["QUJD", "REVG"、"Rw=="]
t.join("");
// QUJDREVGRw==

6 ビットのビット列は、それぞれ次のよいに変換表で定義された英数字(A-Za-z0-9)+ 2 文字に変換される。

000000A
000001B
000010C

Base64 には複数種類あり、追加 2 文字の種類や=で詰めるかなど異なる。

JWT と window.atob()が利用する Base64 は異なる#

window.atob()や window.btoa()JWT
仕様https://datatracker.ietf.org/doc/html/rfc4648#section-4: base64 (standard)https://datatracker.ietf.org/doc/html/rfc4648#section-5: base64url (URL- and filename-safe standard)
111110+-
111111/_

このため JWT と window.atob では Base64 の差分である _- がある場合、エラーが出てしまう。

atob("LCJz...dWIiOiLjgZXjgY_jgonjg5H...jg7");
// Failed to execute 'atob' on 'Window': The string to be decoded is not correctly encoded.

この問題は Latin 文字かどうかに限らず発生するように思えるかもしれないが、Latin 文字で 1 が 5 個以上連続することは恐らくほぼないため、日本語を利用しているケースでエラーが発生した。

日本語でエラーが出ていないケースでは、追加 2 文字が含まれていないだけで、atob の実行結果は文字化けしていた。

Q. JWTから渡されたBase64文字列をwindow.atob()に渡すとエラーが出る場合がある
https://blog.ohirunewani.com/posts/"jwt-base64-window-atob-error"/
作者
hrdtbs
公開日
2024-01-04
ライセンス
CC BY-NC-SA 4.0