Base64

バイナリデータを ASCII 文字列に変換するエンコーディング方式。A-Z, a-z, 0-9, +, / の 64 文字を使用する。

Base64 は、バイナリデータを ASCII 文字列に変換するエンコーディング方式です。A-Z (26 文字)、a-z (26 文字)、0-9 (10 文字)、+、/ の計 64 文字を使い、3 バイトのデータを 4 文字で表現します。入力データが 3 の倍数でない場合は、末尾に = (パディング) が付加されます。

Base64 が必要とされる理由は、電子メールや HTTP など、テキストベースのプロトコルではバイナリデータをそのまま送信できないためです。Base64 は電子メールの添付ファイル (MIME)、データ URI スキーム (data:image/png;base64,...)、JWT (JSON Web Token)、API のリクエスト/レスポンスボディなど幅広い場面で使われています。たとえば、小さなアイコン画像を HTML に直接埋め込む際にデータ URI として Base64 エンコードすれば、HTTP リクエスト数を削減できます。

JavaScript では btoa() でエンコード、atob() でデコードできます。ただし、これらの関数は Latin-1 文字のみを扱えるため、日本語などのマルチバイト文字を含む場合は TextEncoder と組み合わせる必要があります。Node.js では Buffer.from(data).toString('base64') を使います。Python では base64 モジュール、Java では java.util.Base64 クラスが標準で提供されています。

よくある誤解として、Base64 を暗号化と混同するケースがあります。Base64 はあくまでエンコーディング (可逆変換) であり、セキュリティ上の保護は一切提供しません。誰でも簡単にデコードできるため、パスワードや機密情報の保護には使えません。また、データサイズは約 33% 増加する点にも注意が必要です。3 バイトが 4 文字になるため、元データの 4/3 倍のサイズになります。データ URI で画像を埋め込むとリクエストは減りますが、その分 HTML が膨らみ、画像単体としてキャッシュできなくなります。判断基準としては、数 KB 程度までのアイコンや区切り線に限り、写真のような大きな画像はファイルのまま配信するほうが有利です。

URL やファイル名に Base64 文字列を含める場合、標準の + と / は特殊文字として扱われるため問題が生じます。この対策として、+ を -、/ を _ に置き換えた base64url が使われます。仕様の出所は RFC 4648「The Base16, Base32, and Base64 Data Encodings」(2006 年・RFC 3548 を廃止) で、標準の Base64 は第 4 節、base64url は第 5 節で定義されています。JWT の署名部で使われる形式もこれで、JWS の仕様 (RFC 7515・2015 年) は base64url を用いた上で末尾の = をすべて省き、改行や空白を一切含めないと定めています。JWT のトークンに = が見当たらないのはこのためです。

実装で足をすくわれやすいのが改行の扱いです。RFC 4648 は、参照する仕様が明示的に指示しない限り base64 の出力に改行を入れてはならないと定めています。一方で電子メールの MIME は 76 文字、PEM 形式の証明書や鍵は 64 文字で行を折るため、これらから取り出した文字列には改行が混ざります。RFC 4648 は同時に、デコーダはアルファベット外の文字 (base64 のアルファベットは英数字 62 文字に + と / を加えた 64 文字で、これとパディングの = 以外) を含むデータを拒否しなければならないとも定めているため (MIME のように無視を許す仕様が別途ある場合を除く)、改行付きの文字列を厳格なデコーダに渡すとエラーになります。コピー&ペーストした鍵やトークンがデコードできないときは、まず改行と前後の空白を疑うとよいでしょう。

文字数カウントの観点では、Base64 エンコード後の文字列長は元データのバイト数から正確に計算できます。パディングを付ける標準形は ceil(n / 3) * 4 文字、パディングを省く形は ceil(4n / 3) 文字です。100 KB (102,400 バイト) の画像なら 136,536 文字で、これをそのまま HTML に埋め込めばページの文字数はその分だけ増えます。行を折る形式では改行も 1 文字ずつ数えられるため、76 文字ごとに折った 136,536 文字の出力には約 1,800 個の改行が加わります。文字数とバイト数を突き合わせる場面では、改行込みで数えているかどうかを先に確認しておくと差分の原因を追いやすくなります。

この記事を共有