Base64 エンコーディングを理解する — 完全な開発者ガイド
Base64 エンコーディングはウェブ開発のほとんどすべての領域に現れます — JWT トークン、データ URI、メールの添付ファイル、API ペイロード、暗号署名、設定ファイル。普及しているにもかかわらず、しばしば誤解されています: 開発者は時々それを暗号化と混同したり、事態を悪化させる状況で使用したりします。このガイドでは、Base64 がどのように動作するか、いつ手を伸ばすか、いつ避けるか、そして JavaScript でどのように正しく使用するかを正確に説明します。
Base64 エンコーディングとは何か (そして何でないか)
Base64 はバイナリからテキストへのエンコーディングスキームです。唯一の仕事は、任意のバイナリデータを印刷可能な ASCII 文字の文字列に変換することです。データを圧縮せず、データを暗号化せず、データを検証しません。これは純粋に表現の変換です — null バイト、制御文字、またはテキストベースのプロトコルを壊す値を含む可能性のあるバイトを取り、安全な文字セットに変換します。
名前は、エンコーディングアルファベットとして使用される 64 個の印刷可能な文字から来ています: A–Z (26 文字)、a–z (26 文字)、0–9 (10 文字)、加えて + と / (2 文字)。= 文字はパディングに使用されます。各文字位置で 64 の可能な値があり 2^6 = 64 であるため、各 Base64 文字は元のデータの正確に 6 ビットをエンコードします。
Base64 はどのように動作するか — 「Hello」を段階的に
Base64 を理解する最良の方法は、具体例を追うことです。文字列 Hello をエンコードしてみましょう。
ステップ 1: バイトに変換
Hello の各文字は ASCII コードにマッピングされ、その後バイナリ (バイトごとに 8 ビット) で表現されます:
H e l l o
72 65 6C 6C 6F (ASCII / 16 進)
01001000 01100101 01101100 01101100 01101111 (バイナリ) ステップ 2: 6 ビットチャンクにグループ化
すべてのビットを連結します: 0100100001100101011011000110110001101111 (40 ビット)。Base64 は一度に 3 バイト (24 ビット) を処理し、それらを 4 つの Base64 文字 (4 × 6 = 24 ビット) にマッピングします。40 ビットを 6 ビットチャンクにグループ化し、最後のグループをゼロでパディングして埋めます:
010010 000110 010101 101100 011011 000110 1111
18 6 21 44 27 6 (最後のグループを 6 ビットにパディング: 111100 = 60) ステップ 3: Base64 アルファベットにマップ
各 6 ビット値は Base64 アルファベットの文字にマッピングされます (A=0、B=1、... Z=25、a=26、... z=51、0=52、... 9=61、+=62、/=63)。5 バイトは 3 の倍数ではないため、1 つの = パディング文字が追加されます:
インデックス: 18 6 21 44 27 6 60
文字: S G V s b G 8
結果: SGVsbG8= (= はパディング) 結果
"Hello" → "SGVsbG8="
これを Toova Base64 エンコーダー/デコーダーで即座に検証できます — Hello を貼り付け、エンコードをクリックすると、SGVsbG8= が得られます。それを反転するにはデコードをクリックします。
33% のサイズオーバーヘッド
Base64 エンコーディングは常にデータサイズを約 33% 増加させます。計算は単純です: 入力の 3 バイト (24 ビット) が 4 つの Base64 文字 (4 × 6 ビットグループでエンコードされた 24 ビット) を生成します。これは 4/3 の比率、つまり 33.3% のオーバーヘッドです。パディング文字を追加すると、入力長によって真のオーバーヘッドは 33% から 36% の間です。
これはパフォーマンスに大きく影響します。HTML に Base64 データ URI として埋め込まれた 1 MB の画像は、約 1.37 MB になります。すべてのバイナリペイロードを Base64 でエンコードする API は、必要以上に 33% 多くのデータを送信します。短いトークンやチェックサムのような小さな値では、オーバーヘッドは無視できます。大きなファイルでは、実際のコストです。
URL セーフバリアント
標準 Base64 は、最後の 2 つのアルファベット文字として + と / を使用します。両方とも URL で問題があります:
+はクエリ文字列でスペース文字としてデコードされる/は URL のパスセパレーター
URL セーフ Base64 (RFC 4648 セクション 5 で定義された Base64url とも呼ばれる) は、+ を - に、/ を _ に置き換えます。一部の URL パーサーによっても誤解される可能性があるため、URL セーフコンテキストでは通常パディング (=) が省略されます。
JWT トークンはパディングなしの Base64url を使用します。JWT ヘッダーやペイロードを手動でデコードするときは、文字置換と欠落したパディングの両方を処理する必要があります。JavaScript でその方法は次のとおりです:
// URL セーフ Base64 (+ を - に、/ を _ に置き換え)
function toBase64Url(base64) {
return base64.replace(/\+/g, '-').replace(/\//g, '_').replace(/=/g, '');
}
function fromBase64Url(base64url) {
const padded = base64url.padEnd(
base64url.length + (4 - base64url.length % 4) % 4,
'='
);
return atob(padded.replace(/-/g, '+').replace(/_/g, '/'));
} Toova の Base64 エンコーダー/デコーダーは、単一のトグルで標準と URL セーフの両方のバリアントをサポートします。
JavaScript での Base64
ブラウザ: btoa() と atob()
ブラウザは 2 つの組み込み関数を提供します: btoa() (バイナリから ASCII、つまりエンコード) と atob() (ASCII からバイナリ、つまりデコード)。混乱する名前順にもかかわらず、それらは 10 年以上ブラウザで利用可能です。
// ブラウザ — atob / btoa (文字列のみ、ASCII セーフ)
const encoded = btoa("Hello");
console.log(encoded); // "SGVsbG8="
const decoded = atob("SGVsbG8=");
console.log(decoded); // "Hello"
重要な制限: btoa() は Latin-1 範囲 (コードポイント 0–255) の文字を持つ文字列のみを受け入れます。絵文字や CJK 文字のような Unicode 文字を含む文字列を渡すと、DOMException をスローします。任意のバイナリデータをエンコードするには、まず Uint8Array に変換します:
// ブラウザ — 任意のバイナリをエンコード (Uint8Array)
const bytes = new Uint8Array([72, 101, 108, 108, 111]);
const encoded = btoa(String.fromCharCode(...bytes));
console.log(encoded); // "SGVsbG8="
Unicode 文字を含む可能性のある任意の文字列をエンコードするには、推奨されるモダンなアプローチは、最初に TextEncoder を使って Uint8Array を取得し、上記のようにエンコードすることです。
Node.js: Buffer
Node.js は Buffer クラスを提供し、バイナリデータを正しく処理し、Base64 と Base64url を含む複数のエンコーディングをサポートします:
// Node.js — Buffer (バイナリを安全に処理)
const encoded = Buffer.from('Hello').toString('base64');
console.log(encoded); // "SGVsbG8="
const decoded = Buffer.from('SGVsbG8=', 'base64').toString('utf8');
console.log(decoded); // "Hello"
// Node.js での URL セーフバリアント
const urlSafe = Buffer.from('Hello').toString('base64url');
console.log(urlSafe); // "SGVsbG8" (パディングなし) base64url エンコーディングオプション (Node.js 16 以降で利用可能) は、文字置換とパディング削除を自動的に処理し、変換を手動で行うよりはるかに簡単にします。
大きなバイナリファイルを処理する必要があるブラウザ環境では、FileReader.readAsDataURL メソッドが、すべてを一度にメモリにロードせずにファイルを Base64 データ URI としてエンコードします。
いつ Base64 を使うか
テキストのみのプロトコルでのバイナリデータの埋め込み
Base64 の元のユースケースは、7 ビット ASCII テキストのみをサポートするプロトコルである SMTP でのバイナリメール添付ファイルのエンコーディングでした。生のバイトを処理できない形式にバイナリデータを含める必要があるあらゆる場所に同じ原則が適用されます: JSON API ペイロード、XML ドキュメント、HTML 属性、CSS 値、HTTP ヘッダー。
小さなアセットのためのデータ URI
CSS と HTML は、画像、フォント、SVG を Base64 データ URI として埋め込むことを許可します。これにより、アイコンのような小さなアセットの HTTP 往復が排除され、重要な above-the-fold 画像のための未スタイルコンテンツのフラッシュが排除されます。
<!-- Base64 データ URI としてのインライン SVG アイコン -->
<img src="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0c..." alt="icon" />
<!-- インライン CSS 背景 -->
.icon {
background-image: url("data:image/png;base64,iVBORw0KGgo...");
} トレードオフ: Base64 データ URI は、それらを含む HTML/CSS ファイルから別々にキャッシュできません。画像が変わらないが周囲の HTML が変わる場合、ブラウザはページ再読み込みごとに画像データを再ダウンロードします。データ URI は、往復の排除がキャッシュペナルティを上回る、小さなアセット (理想的には 4 KB 未満) にのみ使用してください。
JSON または URL パラメーターのバイナリデータのエンコーディング
JSON はテキスト形式です — 生のバイナリバイトを直接表現できません。API がバイナリデータ (画像サムネイル、暗号署名、圧縮データ) を送信する必要があるとき、Base64 が JSON ペイロードに含めるための標準的な方法です。同様に、URL クエリパラメーターでバイナリデータを渡す必要がある場合、Base64url エンコーディングは、データが破損なくパーセントエンコーディングを通過することを保証します。
JWT とその他のトークン形式
JWT トークンは Base64url を使用してヘッダーとペイロードセクションをエンコードします。これにより、トークンは HTTP ヘッダー、Cookie、または URL パラメーターで渡せる印刷可能な URL セーフな文字列になります。エンコーディングはセキュリティのためではありません (ペイロードはトークンを持つ誰でも読める) — それは純粋に安全な送信のためです。
いつ Base64 を使わないか
セキュリティや機密性
Base64 はゼロセキュリティを提供します。ミリ秒単位で簡単に可逆です。パスワード、API キー、または機密設定値を「難読化」するために使用しないでください。Base64 文字列を見る開発者は誰でもすぐにデコードします。機密性が必要な場合は、暗号化を使用してください。
パスワード保存
Base64 でエンコードされたパスワードを保存することは、平文で保存することと同じです — エンコーディングは即座に可逆です。パスワードは bcrypt、Argon2、scrypt のような適切なパスワードハッシュ関数でハッシュ化する必要があります。
大きなバイナリファイル
10 MB のファイルを Base64 としてエンコードすると、13.7 MB の文字列が生成されます。それをデータベース列に保存したり、それを通して検索したり、API 経由で送信したりすると、毎回 33% のオーバーヘッドを支払います。大きなバイナリデータには、専用のバイナリストレージを使用します: データベース BLOB/BYTEA 列、S3 や GCS のようなオブジェクトストレージ、またはバイナリを直接ストリーミングします。
バイナリを直接使用できる状況
プロトコルや形式が生のバイナリをサポートする場合 — たとえば、バイナリメッセージタイプの WebSocket、multipart/form-data HTTP アップロード、またはバイナリファイル形式 — バイナリを直接使用します。Base64 は、輸送媒体が本当に生のバイトを処理できないときにのみ必要です。
よくある落とし穴
エンコーディングと暗号化の混同
これは最も一般的な間違いです。Base64 は可視です。これはセキュリティメカニズムではありません。「パスワードはセキュリティのために Base64 エンコードされて保存されている」のようなコードコメントは、コードレビューで捕捉されるべき深刻な誤解を示しています。
Unicode 文字列での btoa() の使用
コードポイントが 255 を超える文字を含む文字列に対して btoa() を呼び出すと、DOMException: Failed to execute 'btoa': The string to be encoded contains characters outside of the Latin1 range がスローされます。Unicode 文字を含む可能性のある文字列をエンコードする前に、常に TextEncoder を介して Uint8Array に変換します。
デコード時のパディングを忘れる
Base64 文字列は 4 の倍数の長さを持つ必要があります。Base64 文字列がパディングなしで生成された場合 (URL セーフエンコーディングで一般的)、デコードする前に正しい数の = 文字を追加する必要があります。長さ n の Base64 文字列には (4 - n % 4) % 4 パディング文字が必要です。これを忘れると、診断が難しいデコードエラーが発生します。
二重エンコーディング
Base64 文字列自体は有効な ASCII であるため、btoa(btoa(data)) はエラーなく動作しますが、二重エンコードされた出力を生成します。複数の層のシリアル化 (たとえば、JSON 内の JSON) を介して Base64 値を渡すとき、同じデータを 2 回エンコードしやすいです。常にエンコードした正確な回数だけデコードしてください。
クイックリファレンス: 実践での Base64
コードを書かずにブラウザでエンコードおよびデコードするには、Toova Base64 エンコーダー/デコーダーがブラウザ内で完全に動作します — サーバー往復なし。標準と URL セーフのバリアント、バイナリファイルをエンコードするためのファイルアップロード、デコードされたデータのテキストと 16 進の両方の出力をサポートします。
URL 内のエンコードされたコンテンツを扱う場合、URL エンコーダー/デコーダーは Base64 とは別にパーセントエンコーディングを処理します。HTML エンティティには、HTML エンティティ変換ツールが HTML コンテキストで文字エスケープを処理します。これらは異なるエンコーディングスキームです — それぞれが特定のユースケースを持っています。
Base64 の標準リファレンスは RFC 4648 で、標準 Base64 (セクション 4)、Base64url (セクション 5)、Base32 (セクション 6–7) を定義しています。btoa() と atob() ブラウザ API については、MDN の btoa() ドキュメントがブラウザ互換性と Unicode 制限を詳細にカバーしています。
まとめ
Base64 エンコーディングは、64 文字のアルファベットを使ってバイナリデータを印刷可能な ASCII に変換します。データサイズを 33% 増加させ、完全に可逆で、セキュリティを提供しません。テキストベースの形式にバイナリデータを埋め込む必要があるときに使用します — JSON ペイロード、HTML データ URI、JWT トークン、メールの添付ファイル、URL パラメーター。セキュリティが必要な場合、トランスポートがバイナリを直接サポートする場合、または 33% のオーバーヘッドが大規模に重要な場合は避けてください。
Base64 が何であるか — そして何でないか — を理解することで、最も一般的な間違いを防ぎます: セキュリティに使用すること、大きなファイルに不必要に適用すること、そして URL エンコーディングや HTML エンティティのような他のエンコーディングスキームと混同すること。各エンコーディングスキームは特定の問題を解決します。Base64 は正確に 1 つを解決します: バイナリデータをテキストのみのチャネルに対して安全にすることです。
エンコードまたはデコードする準備ができましたか?Toova Base64 エンコーダー/デコーダーをお試しください — テキストを貼り付けるかファイルをドロップし、標準または URL セーフを切り替え、結果をコピーします。アカウントなし、サーバーなし、制限なし。