516 文字
3 分
Q. JWTから渡されたBase64文字列をwindow.atob()に渡すとエラーが出る場合がある
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 文字に変換される。
000000 | A |
---|---|
000001 | B |
000010 | C |
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"/