手軽屋
ツール一覧

Base64の仕組みとUTF-8対応 — btoa/atobの落とし穴と回避策

Base64の正体は「3バイトを4文字に変換」する符号化方式。RFC 4648で定義された仕組みと、JavaScriptで日本語を扱うときによく出るInvalidCharacterErrorの原因・回避策を実装コードつきで解説します。

1. Base64の3バイト→4文字変換の仕組み

RFC 4648 Section 4 はこう定義:「3つの8ビット入力グループを連結した24ビットを、6ビット×4の連結グループとして扱い、各6ビットをBase64アルファベットの1文字へマッピングする」。

つまり24ビットを4分割して0〜63の値を出し、その値を64文字のアルファベットから引きます。3バイトが4文字になるので、データサイズは約4/3倍(33%増)になります。

2. Base64アルファベット(RFC 4648 Table 1)

公式の対応表(Value→Encoding):

合計65文字のうち64文字がデータ、1文字(=)がパディング専用。これがRFC 4648 Section 4 の「A 65-character subset of US-ASCII is used」の意味です。

3. パディング「=」の役割(Section 3.2)

3で割り切れない長さの入力を処理する仕組み:

RFC 4648 Section 3.5は「pad bitsは0でなければならない」と規定。たとえば1バイト入力時、最後の6ビットのうち先頭2ビットだけが入力データで、残り4ビットは0で埋めるという意味。これが守られないと、同じデータから複数の異なるBase64文字列が生成されてしまい、canonical(正規化された)形でなくなります。

4. JavaScript btoa/atob の制約

ブラウザのbtoa(s)は文字列sを「各文字が1オクテット(Latin1範囲)」とみなしてBase64化します。日本語の「あ」(UTF-16のコードユニットで0x3042)を渡すと、コードユニットがLatin1範囲を超えているのでInvalidCharacterErrorが投げられます。

つまりbtoa("あ")は失敗する。btoa(unescape(encodeURIComponent("あ")))という古いハックはあるけれど、現代ではTextEncoder/TextDecoderを使うのが正解です。

5. UTF-8対応のBase64エンコード実装

手軽屋のツールが採用しているコード(要旨):

// UTF-8でバイト列に → 8bit文字列に → btoa
const b64encode = (s) => {
  const bytes = new TextEncoder().encode(s);
  let bin = "";
  bytes.forEach((b) => (bin += String.fromCharCode(b)));
  return btoa(bin);
};

// atob → Uint8Array → TextDecoder("utf-8")
const b64decode = (s) => {
  const bin = atob(s.replace(/\s/g, ""));
  const bytes = Uint8Array.from(bin, (c) => c.charCodeAt(0));
  return new TextDecoder("utf-8", { fatal: true }).decode(bytes);
};

ポイント:(1) TextEncoderはUTF-8固定。(2) 8bit文字列の中継はLatin1範囲だけでASCII以外もbtoaが受理する条件を満たすため。(3) decode側でfatal: trueを指定すると、壊れたバイト列のときに例外を投げてくれる。

6. Base64URLとの違い(RFC 4648 Section 5)

URLやファイル名に直接埋め込むときは「Base64URL」(RFC 4648 Section 5)を使います。通常のBase64との違い:

JWT(JSON Web Token)・OIDC・データURI・ファイル名など、URL系の文脈ではBase64URLが標準。本ツールの出力からBase64URLが必要なときは、置換で対応してください:

const toBase64URL = (s) =>
  s.replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");

7. 折り返し(76文字制限)の罠

MIME(メール本文の符号化)では、Base64出力を76文字ごとに改行で折り返す慣習があります。これは送信プロトコル(SMTP)の行長制限が由来。RFC 4648 Section 3.1 は「明示的に指示されない限り改行を入れてはならない(MUST NOT)」と規定。

そのため、URLやDB列にBase64を保存するときは折り返さない方が安全。メール用途なら別途76文字折り返しが必要。本ツールは折り返しなしを採用(RFC 4648準拠)。

8. 関連ツール・記事

Base64変換ツールを使う