Base64は暗号化ではない
Base64エンコードがセキュリティを一切提供しない理由を解説。エンコーディング、暗号化、ハッシュ化の決定的な違いと、それぞれの適切な使い方を紹介します。
Concept
詳細な説明
ソフトウェア開発で最もよくある危険な誤解の一つが、Base64をセキュリティの手段として扱うことです。Base64はエンコーディングスキームであり、暗号化ではありません。機密性は一切提供されず、誰でもBase64文字列を瞬時にデコードできます。
根本的な違い:
エンコーディング(Base64、16進数、URLエンコード):
- 目的:互換性のためにデータを別の形式で表現する
- 可逆性:鍵やシークレットなしで誰でもデコードできる
- セキュリティ:なし。ピッグラテンでメッセージを書くのと同程度の秘匿性
暗号化(AES、RSA、ChaCha20):
- 目的:データの機密性を保護する
- 可逆性:正しい鍵がある場合のみ復元可能
- セキュリティ:鍵なしでの復元は計算上不可能
ハッシュ化(SHA-256、bcrypt、Argon2):
- 目的:データの固定サイズのフィンガープリントを作成する
- 不可逆:ハッシュから元のデータを復元することは不可能
- セキュリティ:データの完全性検証やパスワード保存に使用
危険な実例:
// WRONG: This is NOT secure
const "encrypted" = btoa("password123");
// "cGFzc3dvcmQxMjM=" -- anyone can decode this
// RIGHT: Use actual encryption
const key = await crypto.subtle.generateKey(
{ name: "AES-GCM", length: 256 }, true, ["encrypt", "decrypt"]
);
const iv = crypto.getRandomValues(new Uint8Array(12));
const encrypted = await crypto.subtle.encrypt(
{ name: "AES-GCM", iv }, key, new TextEncoder().encode("password123")
);
この間違いがよく見られる場面:
- データベースにパスワードをBase64で保存(代わりにbcryptやArgon2を使用すべき)
- クライアントサイドコードでAPIキーをBase64エンコードして「隠す」(ブラウザのDevToolsで即座に露出)
- HTTP Basic認証の資格情報がBase64エンコードされているため安全だと思い込む(HTTPSを使用している場合のみ安全)
- 設定ファイルの機密データをBase64で難読化する(適切なシークレット管理として環境変数、Vault、AWS Secrets Managerを使用すべき)
- Kubernetes Secretsは名前に反して値をBase64で格納しているだけであり、デフォルトでは暗号化されていない
Base64がセキュリティと共に正しく使用される場面:
- JWTトークン:ペイロードは転送のためにBase64urlエンコードされるが、署名が完全性の検証を提供する(機密性ではない)
- TLS/HTTPS:トランスポート層を暗号化し、その中でBase64エンコードされたデータが安全に転送される
- PEM証明書:証明書データはテキスト表現のためにBase64エンコードされるが、暗号的なセキュリティは鍵ペアから得られ、エンコーディングからではない
原則: 「これをBase64エンコードして安全にしよう」と考えている自分に気づいたら、即座に立ち止まり、適切な暗号化またはハッシュ化を使用してください。
ユースケース
開発者がBase64エンコードを暗号化の代替として機密ユーザーデータの保存に誤用しているケースを特定するためのセキュリティ監査を実施する場合に参考になります。