「Base64 でエンコードしておけば誰も読めない」 —— このフレーズはコードレビュー、Slack、Stack Overflow の回答に、必要以上の頻度で現れます。Base64 は暗号化ではありません。難読化でもありません。いかなる種類のセキュリティでもありません。デコードは 1 回の関数呼び出しで済み、鍵や秘密は不要です。本稿は Base64 が実際何か、なぜ誤解が根強いのか、データを本当に守りたいときに何を使うべきかを説明します。
Base64 とは実際何か
Base64 はエンコーディング方式 です —— バイナリデータと 64 種の印刷可能 ASCII 文字(A–Z、a–z、0–9、+、/、= をパディングに)との可逆マッピングです。
入力 3 バイトごとに 4 つの Base64 文字になります。アルゴリズムは完全に決定的で、鍵を必要としません:
// エンコード
btoa("hello") // → "aGVsbG8="
btoa("secret password") // → "c2VjcmV0IHBhc3N3b3Jk"
// デコード —— 1 回の呼び出し、鍵不要
atob("c2VjcmV0IHBhc3N3b3Jk") // → "secret password"これだけです。エンコードされた文字列にアクセスできる人なら、どの言語・環境でも追加情報なしに即座にデコードできます。
誤解が存在する理由
Base64 エンコード文字列は暗号化データのように見えます。大文字・小文字、数字、特殊文字の混合で、元の入力と目に見える類似はありません。この視覚的ノイズは非技術者を欺くのに十分で、驚くほど頻繁に技術者も欺きます。
このパターンは近接性によっても強化されます。Base64 はセキュリティ周辺に絶え間なく現れます:
- HTTPS 証明書は Base64 エンコード(PEM 形式)
- JWT トークンの header と payload は Base64url 使用
- HTTP Basic 認証は資格情報を Base64 で送信
- SSH 鍵は Base64 で保管
- 暗号化された暗号文も Base64 で転送
これらセキュリティ隣接の場所で Base64 を繰り返し見ると、開発者はエンコーディングとセキュリティを混同します。エンコーディングはラッパに過ぎず、セキュリティは下層の暗号操作から来ます。
エンコーディング vs 暗号化:中核の違い
違いはシンプルで絶対です:
| エンコーディング(Base64) | 暗号化(AES、RSA…) | |
|---|---|---|
| 目的 | バイナリをテキストとして表現 | 未認可者からデータを隠す |
| 鍵が必要? | いいえ | はい |
| 誰でも逆操作可? | はい —— 1 回の関数呼び出し | 正しい鍵がある場合のみ |
| 機密性を提供? | いいえ | はい(正しく使えば) |
| 出力サイズ | 入力より約 33% 大 | アルゴリズム依存 |
暗号化は鍵でデータを変換し、鍵なしで原文を復元するのを計算上実行不可能にします。Base64 エンコーディングにはそうした性質はありません。
現実のセキュリティミス
ソースコード内の「エンコード済み」秘密
// ❌ これは秘密ではない —— 誰でもデコード可
const API_KEY = atob("c2stbGl2ZS1hYmMxMjM0NTY3ODk=");
// このコードを読んだ人は実行:
atob("c2stbGl2ZS1hYmMxMjM0NTY3ODk=") // → "sk-live-abc123456789"自動シークレットスキャナ(GitHub のシークレットスキャン、GitGuardian、truffleHog)は検出の一環として Base64 文字列をデコードします。Base64 でそれらを止められると思っているなら、止められません。
HTTP Basic 認証
// Authorization ヘッダはこんな感じ:
Authorization: Basic dXNlcjpwYXNzd29yZA==
// その Base64 は簡単にデコード:
atob("dXNlcjpwYXNzd29yZA==") // → "user:password"HTTP Basic 認証は HTTPS 上でのみ安全 —— 実際の暗号化は TLS 層が提供します。ヘッダの Base64 は純粋に username:password のペアを単一テキスト値としてエンコードするためで、隠すためではありません。平文 HTTP では、トラフィックを傍受する誰もが資格情報を即座に読めます。
JWT「トークン」を秘密扱いする
// JWT はドットで連結された 3 つの Base64url のように見える:
eyJhbGciOiJIUzI1NiJ9.eyJ1c2VySWQiOjQyfQ.abc123
// 真ん中(payload)は自明にデコード:
atob("eyJ1c2VySWQiOjQyfQ") // → '{"userId":42}'JWT payload は暗号化されていません —— Base64url エンコードと署名です。トークンを取得した人は payload を読めます。署名は改竄を防ぎますが、機密性は提供しません。JWE(JSON Web Encryption)を使わない限り、機密データ(パスワード、クレジットカード番号、個人情報)を JWT payload に入れないでください —— JWE は別の、はるかにマイナーな標準です。
URL でデータを難読化
// レガシーアプリの典型パターン
/profile?data=eyJ1c2VySWQiOjQyfQ==
// どのデコーダにも貼れる:
atob("eyJ1c2VySWQiOjQyfQ==") // → '{"userId":42}' URL の Base64 はセキュリティをゼロも加えません。簡単に壊れもします —— 標準 Base64 の + と / は URL セーフでなくパーセントエンコードが必要なので、Base64url という変種(+ → -、/ → _、パディングなし)が存在します。
Base64 の本来の用途
Base64 には正当で重要な用途があります —— セキュリティではありませんが:
- テキスト形式へのバイナリデータ埋め込み —— 画像、フォント、ファイルを
data:URI として HTML/CSS に、または JSON API レスポンスに添付 - メール添付 —— MIME エンコーディングは 7 ビット ASCII しか扱えないプロトコル上でバイナリを Base64 で送信
- 転送安全 —— 一部のチャネルはバイナリ(null バイト、制御文字)を壊します;Base64 は内容が無傷で通ることを保証
- 不透明識別子 —— ID やカーソルを Base64 エンコードすると API 利用者に「これを解析するな」と伝えます(セキュリティは提供しません)
本当にセキュリティが必要なときに使うべきもの
目標が未認可者によるデータアクセスの防止なら、本物の暗号ツールを使ってください:
| 目標 | 使うもの |
|---|---|
| 保存中データの暗号化 | AES-256-GCM(対称) |
| 転送中データの暗号化 | TLS 1.3(HTTPS) |
| パスワードハッシュ | bcrypt、scrypt、Argon2 —— SHA-256 単独はダメ |
| トークン署名 | HMAC-SHA256(JWT HS256)または RS256 |
| 秘密の保管 | 環境変数、シークレットマネージャ(Vault、AWS Secrets Manager) |
| トークンのエンドツーエンド暗号化 | JWE(JSON Web Encryption)—— 通常の JWT ではない |
本当に 暗号化トークンが必要な時:JWE と JOSE
標準 JWT は署名(JWS)であって暗号化ではない —— payload は読める。クレームの機密性が本当に必要なら、JWE(JSON Web Encryption、RFC 7516)を使ってください。JWE は JOSE 標準ファミリーの一部:
- JOSE アルゴリズム —— コンテンツ暗号(例:
A256GCM)と鍵管理(例:RSA-OAEP-256、ECDH-ES、A256KW)。「alg」はコンテンツ暗号鍵のラップ方法、「enc」は payload 自身の暗号化方法を選択。 - 鍵ラッピング —— JWE はメッセージごとに新鮮なコンテンツ暗号鍵を生成し、受信者の鍵でラップ(非対称または対称)。ラップされた鍵はトークンヘッダに乗ります。同じ平文を本文を再暗号化せず異なる受信者向けに暗号化でき ます。
- JWE コンパクト形式 —— ドットで連結された 5 つの Base64url 部分(header.key.iv.ciphertext.tag)、見た目は JWT だが 4 ピースではなく 3 ピース。
信頼できるライブラリ(jose、node-jose、python-jose)を選び、JWE を自作しないこと —— アルゴリズムと鍵形式の組み合わせは繊細で、弱いアルゴリズムへの暗黙の後退は古典的な JOSE バグです。
自分のコードの素早いテスト
コードベースで btoa(、atob(、Buffer.from(…, 'base64') を検索。各箇所で問いましょう:
- テキストをフィールドに収める または転送で生き残らせる ためか?→ 問題なし。
- データを誰かから隠す ためか?→ 本物の暗号化やアクセス制御に置き換えてください。
よくある質問
Base64 は暗号化ですか?
いいえ。Base64 は鍵のない可逆エンコーディング で、誰でも 1 回の関数呼び出し(atob())でデコード可能。暗号化は鍵を要し、鍵なしの復元は実行不可能にします。
Base64 でパスワードや API キーを保管しても安全?
いいえ。デコードは自明で、シークレットスキャナは自動で Base64 をデコードします。シークレットマネージャや環境変数を使い、パスワードは bcrypt、scrypt、Argon2 でハッシュ化してください。
JWT の payload は誰でも読めますか?
はい。JWT payload は Base64url エンコードと署名 であって暗号化ではない —— トークンを持つ誰もがすべてのクレームを読めます。JWE を使わない限り、JWT に秘密を入れないこと。トークンをオンラインツールに貼るのは危険;機密 JSON をオンラインに貼るべきでない理由。
Base64 は本当は何のため?
バイナリを安全にテキストとして表現するため —— data: URI、メール(MIME)添付、ASCII しか扱えないチャネルでの転送。ラッパであって、セキュリティ手段ではない。
ブラウザでエンコード・デコード
今すぐ Base64 文字列を検査したい?fixjson.org の Base64 Encode & Decode で、任意の文字列を即座にエンコード/デコードできます —— 完全な Unicode と UTF-8 を含み、何もインストール不要。JWT payload のデバッグ、API レスポンス検査、data: URI の中身確認に便利です。
- Base64 Encode & Decode —— 即座、ブラウザ内、データはサーバに送らない
- RFC 4648:Base64 標準 —— Base64 と Base64url の歴史と正式定義
- JWT のデコード方法 —— トークンのクレームを読む、なぜデコードが検証ではないか
- RFC 7519:JSON Web Token(JWT) —— Base64url が JWT 標準でどう使われるか、JWT payload のセキュリティ考慮
- URL Decode —— URL やクエリ文字列をパーセントデコード
- JSON Stringify —— JSON 値をエスケープ済み文字列リテラルにエンコード