[GUIDE] 9 min read

[GUIDE] Base64とは何か、どのように動作するのか?

原理から理解する実践的な解説。Base64の根幹にある6ビットグルーピングの仕組み、なぜ出力が33%大きくなるのか、=パディングはどこから来るのか、そしていつ使うべきかを学びます。

April 2026 | 基礎

// 一文で表す定義

Base64とは、64個の印字可能なASCII文字だけを使ってバイナリデータをテキスト表現する方式です。具体的にはA–Z、a–z、0–9、そして2つの記号(通常は + と /、パディングマーカーとしての =)を使用します。これがすべてです。バイナリの3バイトごとに4つのBase64文字へ再グルーピングされ、それ以外は何も変わりません。

Base64はエンコーディングであり、暗号化ではありません。誰でもミリ秒単位で復元できます。その目的はプライバシーではなく、安全な転送です。Base64を使えば、バイナリペイロード(画像、証明書、PDFファイル、暗号鍵など)を、テキストしか受け付けない場所 — HTML属性、JSON文字列、URL、メール本文、YAML設定、データベースのTEXTカラム、環境変数 — に載せることができます。

// そもそもなぜ必要なのか

テキスト専用チャネルは、認識できないバイトを破棄したり改変したりします。7ビットASCIIしか理解しないメールゲートウェイは、英語以外の文字の最上位ビットをすべて壊してしまいます。JSONパーサーは埋め込まれたヌルバイトを拒否します。スペース、引用符、アンパサンドが直接含まれるURLは、パーセントエンコードしない限り無効です。1990年代のSMTP標準は、メール本文が常に英語のプレーンテキストであることを前提としていましたが、人々は写真やスプレッドシートも送りたかったのです。

Base64はこれを解決するために、既知のあらゆるテキストチャネルを無傷で通過する64文字に出力を制限します。26個の大文字、26個の小文字、10個の数字、そして (a) 印字可能で、(b) 一般的なエスケープ機構で使われず、(c) ASCIIを通してラウンドトリップできるほど十分に区別できる2つの記号です。さらに、これも安全なパディング文字 = も含まれます。

// 6ビットグルーピングの仕組み

Base64の機械的な核心を一行で表すと、次のようになります。入力をビットストリームとして受け取り、6ビットの塊に分割して、それぞれを64文字のアルファベットで参照する。

なぜ6ビットなのでしょうか?2^6 = 64だから、6ビットの塊は正確にアルファベットの1文字に対応します。なぜ7ビットや8ビットではないのか?7ビットでは128の値となり多すぎます(すべてが印字可能ではありません)。8ビットでは単なる生のバイナリに戻ってしまいます。6ビットは、あらゆる可能な値に人間が読める文字が割り当てられるスイートスポットなのです。

入力3バイト = 24ビット = ちょうど6ビットの4グループ = 4つのBase64文字。この「3入力4出力」の比率は固定されています。これがBase64出力が入力の約4/3倍、つまり約33%大きくなる理由です。

// Worked example: encoding the 3 bytes 'Man' (77 97 110)
// ASCII:  M        a        n
// Binary: 01001101 01100001 01101110
// Re-group into 6-bit chunks:
//         010011 010110 000101 101110
// Decimal: 19     22     5      46
// Base64:  T      W      F      u
// Result:  'TWFu'  (stored as 4 ASCII bytes: 84 87 70 117)

// Base64のアルファベット

RFC 4648 §4が標準アルファベットを定義しています。位置0–25はA–Z、26–51はa–z、52–61は0–9、位置62は +、位置63は / です。

URLセーフな派生版(RFC 4648 §5、base64urlとしても知られる)は、+ を - に、/ を _ に置き換えています。これにより、追加のエスケープなしで出力をURLやファイル名に安全に埋め込むことができます。どちらのバリアントも、対応するアルファベットを使えば同一にデコードできます。

// Standard Base64 alphabet (index → character)
// 0–25:  A B C D E F G H I J K L M N O P Q R S T U V W X Y Z
// 26–51: a b c d e f g h i j k l m n o p q r s t u v w x y z
// 52–61: 0 1 2 3 4 5 6 7 8 9
// 62:    +       (or  -  in URL-safe)
// 63:    /       (or  _  in URL-safe)
// pad:   =

// = パディングはどこから来るのか

このアルゴリズムは、3バイトの倍数の入力サイズを想定しています(そうすれば4つのBase64文字にちょうど分割できるからです)。入力長が3の倍数でない場合、1〜2バイトが余り、最後の4文字出力グループを埋めるには不足します。= でパディングすることで、何バイトが不足したかを示します。

• 入力長 mod 3 == 0 → パディングなし(例:'Man' → 'TWFu')。
• 入力長 mod 3 == 1 → パディング文字2つ(例:'M' → 'TQ==')。
• 入力長 mod 3 == 2 → パディング文字1つ(例:'Ma' → 'TWE=')。

// Why 'M' becomes 'TQ==':
// Byte: M = 01001101                   (8 bits)
// Padded to 12 bits with zeros: 010011 010000
// Decimal: 19, 16  →  'T', 'Q'
// Output length must be multiple of 4 → add '==' padding
// Result: 'TQ=='
//
// When decoding, the decoder strips '==' and recovers the first 8 bits,
// discarding the trailing zero bits.

// 33%のオーバーヘッドは正確なのか?

ほぼ正確ですが、厳密には異なります。正確な計算式は、長さnのバイト入力に対して ceil(n / 3) × 4 文字です。100 KBの入力であれば ceil(102400 / 3) × 4 = 136,534 文字となり、100 KBより33.3%大きくなります。実際には、HTTPレベルでgzipやBrotliによる圧縮を加えると、ワイヤーに乗せた際のコストは10〜15%程度に近づきます。Base64テキストはそれなりに圧縮が効くからです(生のバイナリほどではありませんが)。

小さなペイロードではパディングが効いてきます。1バイトのエンコードには4文字(サイズの4倍)、2バイトのエンコードでも4文字かかります。そのため、小さなBase64文字列は入力に比べて意外なほど長く見えるのです。

// Base64が実世界で使われる場所

  • > HTML/CSS内のデータURI — アイコンやロゴをインラインで埋め込む:<img src="data:image/png;base64,…">
  • > JSONとGraphQLペイロード — テキストプロトコル内でバイナリ(ファイルアップロード、サムネイル)を送る必要があるAPI
  • > JWTトークン — JWTの3つのドット区切りパーツはすべてbase64url文字列
  • > HTTP Basic認証 — username:password は Authorization ヘッダーでBase64エンコードされる(TLSなしでは依然として安全ではない!)
  • > メール(MIME) — 添付ファイルは、レガシーな7ビットSMTPサーバーを通過できるようBase64エンコードされる
  • > PEM形式の証明書と鍵 — -----BEGIN CERTIFICATE----- ブロックはBase64エンコードされたDERバイナリをラップする
  • > SSHキー — id_rsa.pub や authorized_keys の各行はBase64エンコードされた公開鍵
  • > データベースのTEXTカラム — BLOB型が使えないとき、Base64を使えばバイナリをテキストとして保存できる
  • > 環境変数 — Kubernetes SecretsはBase64エンコードされている(ただし暗号化ではない)
  • > QRコードとマジックリンク — URLに埋め込んでも安全な短いBase64トークン

// BASE64は暗号化ではない

これは最も一般的な誤解です。Base64はデータの内容を隠しません。公開され十分に文書化されたマッピングを使って、バイナリをテキストに書き換えるだけです。パスワードが cGFzc3dvcmQ= としてBase64エンコードされていても、それは平文の password とまったく同じ脆弱性を持ちます。誰でも1回の関数呼び出しでデコードできます。Base64は16進数と同じように扱ってください。それは表現であって、保護ではありません。

プライバシーが必要なら、まず平文に本物の暗号プリミティブ(AES-GCM、ChaCha20-Poly1305、age、PGP)を適用し、そのうえで暗号文をテキストチャネルで転送する必要があるならBase64エンコードします。Base64のステップはラストマイルであって、セキュリティ層ではありません。

Kubernetes Secretsは典型的な落とし穴です。Kubernetesはシークレット値をBase64エンコードして保存するため、漠然と保護されているように見えます。しかしそうではありません。namespace への読み取りアクセス権を持つ誰もがそれを復元できます。真のシークレット保護には、SealedSecrets、Vault、SOPS、あるいはクラウドネイティブなKMS統合などのツールが必要です。

// 各言語での手早いエンコード/デコード

// JavaScript (browser):
//   btoa('Hello')            → 'SGVsbG8='
//   atob('SGVsbG8=')         → 'Hello'
//   (btoa/atob only handle Latin-1; use TextEncoder for Unicode)
//
// Node.js:
//   Buffer.from('Hello').toString('base64')     → 'SGVsbG8='
//   Buffer.from('SGVsbG8=', 'base64').toString() → 'Hello'
//
// Python:
//   import base64
//   base64.b64encode(b'Hello')       → b'SGVsbG8='
//   base64.b64decode('SGVsbG8=')     → b'Hello'
//
// Go:
//   base64.StdEncoding.EncodeToString([]byte("Hello")) → "SGVsbG8="
//
// Ruby:
//   Base64.strict_encode64('Hello')   → 'SGVsbG8='
//
// Shell:
//   echo -n 'Hello' | base64          → 'SGVsbG8='
//   echo 'SGVsbG8=' | base64 -d        → 'Hello'

// 避けるべき典型的なミス

  • > URLセーフな文字列を標準アルファベットでエンコードする — 受信側のデコーダーは +/ を拒否します。URL、Cookie、ファイル名に出力を載せるときはbase64urlを使いましょう。
  • > 二重エンコード — すでにBase64のテキストをさらにエンコーダーに通してしまう。エンコード前に、そのデータがすでにBase64でないか必ず確認してください。
  • > UTF-8を忘れるbtoa('héllo') はブラウザで例外を投げます。é はLatin-1の範囲外だからです。まず TextEncoder で文字列をバイト列に変換してください。
  • > パディング削除のミスマッチ — base64urlではしばしば = パディングが省略されます。デコーダーが厳格なら、パディングを追加し直してください:input + '==='.slice((input.length + 3) % 4)
  • > Base64が安全だと思い込む — そうではありません。暗号化は別物です。常に。
  • > 巨大なバイナリにBase64を使う — 500 MBのファイルを単一の文字列にエンコードするとメモリが尽きます。ストリーミングで処理しましょう。
  • > 行折り返しあり/なし — MIMEやPEMはBase64を64または76桁で \n 折り返しします。それ以外の多くの文脈では単一行が期待されます。適切に改行を除去または挿入してください。

// 30秒でわかるBase64 — チートシート

  • > 64個の印字可能ASCII文字(A–Z、a–z、0–9、+、/)に加えて、= パディング
  • > 3バイト入力 → 4文字出力(固定比)
  • > 出力は入力より約33%大きい
  • > 可逆:エンコーディングであって暗号化ではない
  • > URLとファイル名にはbase64url(+ → -、/ → _ に置換)を使う
  • > 出力長が4の倍数になるように、短い入力には1〜2個の = でパディングする
  • > Unicodeテキストの場合:文字列をまずUTF-8バイト列にエンコードし、それをBase64化する
  • > 主要言語の標準ライブラリすべてでデコード可能
  • > HTML、JSON、URL、メール、PEMファイル内で安全
  • > セキュリティ層としては安全ではない — 機密データには必ず本物の暗号化と組み合わせる

// 次のステップ

仕組みが分かったら、弊社の Base64エンコーダーBase64デコーダー を試し、文字単位で出力を確認してみてください。URLセーフなペイロードには base64url に切り替えましょう。画像なら 画像 → Base64 をご覧ください。

関連する詳細記事:
Base64 URLセーフ vs 標準 — どちらのアルファベットをいつ使うか
UTF-8とUnicodeのためのBase64 — btoa() の落とし穴を避ける
JavaScriptとNode.jsでのBase64 — atob、btoa、Buffer
Base64 vs Base64URL vs Base32 — 比較表