Base64エンコードとは?仕組みと使い方を実例で解説
Base64エンコードとは、あらゆるデータをA〜Z、a〜z、0〜9、+、/という64種類の印字可能なASCII文字だけで表現する方式です。テキストしか安全に扱えないシステムの中でも、画像やバイナリデータをそのまま通過させられるようにするための仕組みです。暗号化ではなく、可逆な形式変換であり、誰でも一瞬で元に戻せます。
Base64エンコードが実際にしていること
Base64は生のバイト列を、A-Z、a-z、0-9、+、/の64種類の記号からなる固定のアルファベットで書き直します。入力の3バイトごとに、出力はちょうど4文字のBase64文字になるため、エンコード後のデータは元のデータよりおよそ33%大きくなります。
ここでよく誤解されるポイントをはっきりさせておきます。Base64はデータを小さくするのではなく大きくしますし、鍵という概念が一切ありません。パスワードをBase64で「エンコード」してそのまま保存しても、それは保護になっていません。テキストエディタと5秒あれば誰でも元のパスワードに戻せます。Base64が解決するのは転送の問題(このバイト列はテキスト欄やURL、JSON文字列に貼り付けても壊れずに残るか)であって、セキュリティの問題ではありません。秘匿性が必要なら、それはハッシュ化や暗号化の役目であり、Base64とは関係ありません。
仕組み: 「Man」をビット単位でエンコードする
Base64という名前がそのまま仕組みを表しています。これは基数変換です。通常のバイトは256進数(8ビットで256通りの値)ですが、Base64は同じビット列を6ビットずつのまとまりに組み直し、6ビット(0〜63の64通り)ごとに1文字を対応させます。
“Man”というテキストを例にします。3文字のASCII値はM=77、a=97、n=110です。2進数で書くと次のようになります。
| 文字 | バイト値 | 2進数(8ビット) |
|---|---|---|
| M | 77 | 01001101 |
| a | 97 | 01100001 |
| n | 110 | 01101110 |
この24ビットを順番につなげます。
010011010110000101101110
元のバイトの区切りは無視して、このビット列を6ビットずつ4つに分割します。
010011 | 010110 | 000101 | 101110
それぞれの6ビットは0から63までの数値です。10進数に変換し、Base64のアルファベットで対応する文字を調べます。
| 6ビット | 10進数 | Base64文字 |
|---|---|---|
| 010011 | 19 | T |
| 010110 | 22 | W |
| 000101 | 5 | F |
| 101110 | 46 | u |
順番に読むと”Man”は”TWFu”になります。何も不思議なことはありません。3バイト(24ビット)が6ビットずつ4つのグループにきれいに分かれるのは、24が8でも6でも割り切れるからです。だから入力と出力の比率はいつも「3バイト入力、4文字出力」で決まります。
パディング: 末尾に=が付く理由
先ほどのきれいな3バイト区切りは、入力の長さが3の倍数のときだけ成り立ちます。実際のテキストはそうでないことのほうが多いので、Base64には「最後のグループが足りていない」ことを示す仕組みが必要です。それが=というパディング文字です。
| 入力 | バイト数 | Base64出力 | 理由 |
|---|---|---|---|
| ”M” | 1 | TQ== | 1バイトでは3バイトのグループを満たせず、実質2文字分しか作れないため==でパディング |
| ”Ma” | 2 | TWE= | 2バイトから実質3文字が作られ、=を1つパディング |
| ”Man” | 3 | TWFu | 3バイトでちょうどグループが埋まり、4文字ぴったり、パディング不要 |
Base64文字列の末尾に見える=は、最後のグループで何ビット足りなかったかを示す埋め草にすぎません。それ自体にデータとしての意味はありません。
Base64が実際に使われている場面
Base64は、バイナリデータや特殊文字がテキストしか通れない経路を通過する必要がある場所ならどこにでも登場します。具体例をいくつか挙げます。
HTTP Basic認証。 user:pass1234という認証情報はdXNlcjpwYXNzMTIzNA==にエンコードされ、Authorization: Basic dXNlcjpwYXNzMTIzNA==というヘッダーで送られます。これはBasic認証そのものの仕組みであり、同時にBasic認証単体では実質的な保護になっていないことを思い出させてくれる例でもあります(HTTPS上で使わない限り)。
構造化データをペイロードやURLに埋め込む。 {"id":42,"active":true}のようなJSONオブジェクトはeyJpZCI6NDIsImFjdGl2ZSI6dHJ1ZX0=になります。引用符も波括弧もエスケープする必要のないフラットな文字列になるので、URLのクエリパラメータや別のJSONフィールドにそのまま入れられます。
data URL。 CSSやHTMLは、別ファイルを読みに行く代わりにdata:image/png;base64,iVBORw0K…という形で小さな画像を直接埋め込めます。スタイルシートの中に長いBase64の塊が入っているのを見かけるのはこのためです。
メール添付ファイル。 メールの添付方式であるMIMEは数十年前にテキストのみでの転送を前提として設計されたため、バイナリファイルはメール本文に組み込む前にBase64でエンコードされます。
JWTトークン。 JSON Web Tokenはheader.payload.signatureという、ドットで区切られた3つのBase64url区間で構成されています。payload部分はただのBase64url化されたJSONなので、下にあるような普通のBase64/Base64urlデコーダーで問題なく中身を読めます(このツールはJWT専用のデコーダーではなく、貼り付けた区間をそのままデコードするだけです)。
デコーダーの動作確認に便利な例をもう2つ挙げておきます。
“Hello, World!”はSGVsbG8sIFdvcmxkIQ==にエンコードされます。
“café”はY2Fmw6k=になります。これは立ち止まって見る価値があります。“café”は見た目には4文字ですが、éはUTF-8で2バイトを使うため、実際には4文字ではなく5バイトのデータです。Base64が扱うのは常にバイト単位であり、文字単位ではありません。同じことは絵文字の”🚀“にも当てはまり、これ単体でUTF-8の4バイトになり、8J+agA==にエンコードされます。見た目の文字数から想像したより長いBase64出力になっていたら、たいていはこのマルチバイトUTF-8が理由です。
自分のテキストでエンコード・デコードを試す
下のどちらかのウィジェットに入力してみてください。処理はすべてブラウザの中で完結し、サーバーには何も送信されません。
よくある間違いと注意点
Base64を暗号化だと思い込む。 これは繰り返しはっきり書いておく価値があります。Base64はセキュリティ対策ではありません。鍵を持たず、設計上完全に可逆であり、パスワードやトークン、個人情報の保護としてBase64を使うのは些細な技術的誤解ではなく、実害のあるアンチパターンです。秘匿したいデータは暗号化してください。Base64はその役割を担いません。
標準のBase64はURLセーフではない。 +、/、=という文字は、URLやクエリ文字列、ファイル名の中で壊れたり曖昧になったりすることがあります。だからこそBase64urlという別方式が存在し、+と/を-と_に置き換え、パディングを省略することも多くなっています。URLやJWTの中で+も/も=もないBase64っぽい文字列を見かけたら、それがURLセーフ版だからです。標準のBase64とは別物です。
空白や改行、パディングの欠落でデコードが失敗する。 厳格なデコーダーは、余計な改行やスペースが混ざっていたり、貼り付けの過程でパディング文字が削られていたりすると、その文字列を拒否することがあります。デコードが思わぬ形で失敗したら、まず文字列をきれいにしてみてください。
マルチバイトのUTF-8が想定より出力を大きくする。 先ほどの”café”や”🚀“の例で見たとおり、Base64はバイトをエンコードするのであって、目に見える文字をエンコードするのではありません。アクセント記号付きの文字、日本語や中国語などのCJKテキスト、絵文字はいずれもUTF-8で1文字あたり複数バイトを使うため、エンコード後の長さは画面上の文字数ではなくバイト数を反映します。
うっかり二重エンコードしてしまう。 すでにBase64化された文字列をもう一度エンコーダーに通すと、元のBase64の外側にもう一層Base64が重なった状態になります。一度だけデコードしても、まだ意味の分からないBase64の文字列が出てくるだけで、元の中身にはたどり着けません。デコード結果が文字化けに見えたら、文字列が壊れていると決めつける前にもう一度デコードしてみてください。
よくある質問
Base64は暗号化と同じものですか。 いいえ。Base64は鍵を一切持たない可逆なエンコード方式です。特別なツールや知識がなくても、誰でも1ステップでデコードできます。データを秘匿したい場合は本物の暗号化を使ってください。Base64が変えるのは表現方法だけで、機密性は何も変わりません。
なぜBase64の出力は元のテキストより長く見えるのですか。 8ビットのバイトを6ビットの文字に置き換えるため、入力3バイトは必ず出力4文字になり、約33%サイズが増えるからです。このオーバーヘッドは、任意のバイト列をテキストとして安全に扱える少数の文字集合で表現するための代償です。
Base64文字列の末尾にある=は何を意味していますか。 これはパディングで、入力の長さが3バイトの倍数でないときに付きます。=が1つなら最後のグループが1バイト足りなかったことを、==なら2バイト足りなかったことを示します。実際のデータは何も表しておらず、入力がどこで終わったかの目印にすぎません。
URLやJWTの中のBase64っぽい文字列に、なぜ+や/、=がないことがあるのですか。 それはBase64urlという、URLやファイル名、JWTのようなトークン向けに作られた別方式を使っているからです。URLの中で問題を起こしやすい+と/を-と_に置き換え、パディングも省略するのが一般的です。
Base64デコーダーでJWTをデコードできますか。 header部分とpayload部分は、いずれもドットで区切られたBase64url化されたJSONなので、その2つはデコードできます。header部分かpayload部分(トークン全体ではなく、signature部分でもなく)をBase64/Base64urlデコーダーに貼り付ければ、中に入っているクレームを読み取れます。