← 記事一覧

Base64 は暗号化ではない: 開発者がよくする誤解

Base64 でエンコードされた文字列はごちゃ混ぜに見えるが、関数 1 つで誰でもデコードできる。Base64 が実際は何か、暗号化と混同される理由、本当にデータを守るには何を使うかを学ぶ。

「Base64 でエンコードしておけば誰も読めない」 —— このフレーズはコードレビュー、Slack、Stack Overflow の回答に、必要以上の頻度で現れます。Base64 は暗号化ではありません。難読化でもありません。いかなる種類のセキュリティでもありません。デコードは 1 回の関数呼び出しで済み、鍵や秘密は不要です。本稿は Base64 が実際何か、なぜ誤解が根強いのか、データを本当に守りたいときに何を使うべきかを説明します。

Base64 とは実際何か

Base64 はエンコーディング方式 です —— バイナリデータと 64 種の印刷可能 ASCII 文字(A–Za–z0–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-256ECDH-ESA256KW)。「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 の中身確認に便利です。