こんにちは。セキュリティの最前線で泥臭いインシデント対応を続けているエンジニアです。
今回は、開発者が一度は夢見る「ブラウザで暗号化すれば、サーバーに秘密を渡さなくて済むから完璧じゃないか?」という問いに対して、現場のリアルな視点から「なぜそれが危険なのか」を紐解いていきます。
「家の鍵を、泥棒が自由に出入りできる玄関マットの下に置く」ようなミスを避けるために、一緒に学んでいきましょう。
—
1. ブラウザ暗号化の「盲点」:鍵はどこにある?
皆さんがブラウザでJavaScriptを使って暗号化を行うとき、一番の悩みは「暗号化するための鍵(キー)をどこに隠すか」ですよね。
例えば、ユーザーのパスワードから鍵を生成して、それでデータを暗号化するとしましょう。このとき、その「鍵」をJavaScriptの変数に保存したり、localStorage に置いたりしていませんか?
ここに、XSS(クロスサイトスクリプティング)という泥棒が忍び込みます。
XSSという泥棒の正体
XSSは、あなたのサイトに悪意のあるスクリプトを注入して、ブラウザ上で勝手にコードを実行させる攻撃です。
- 反射型: 攻撃者が罠URLを送りつけ、クリックした瞬間に実行される。
- 格納型: 掲示板などに悪意のあるコメントを書き込み、表示した人全員を攻撃する。
- DOM型: ページの見た目を整えるJavaScriptを悪用し、ページ内で完結する攻撃。
どのタイプでも、攻撃者は「あなたのサイトの所有者」と同じ権限でJavaScriptを実行できます。つまり、あなたがJavaScriptで大事に隠していた鍵も、攻撃者には丸見えなんです。玄関マットの下に鍵を置くのは、泥棒に「どうぞ入ってください」と言っているのと同じですよね。
—
2. 鍵管理のベストプラクティス:鍵を「手元」に置かない
「ブラウザで暗号化したい」という要望の多くは、「サーバーに生データを見せたくない」という動機から来ています。この要求を満たすための、現実的な設計指針は以下の通りです。
鍵をブラウザに持たせない(鍵の分離)
理想は、鍵をユーザーの記憶の中(パスワード)にのみ存在させ、メモリ上では最小限の時間しか生存させないことです。
- Web Crypto API を使う:
eval() や怪しいライブラリは論外です。ブラウザ標準の window.crypto.subtle を使いましょう。これは鍵を直接JavaScriptオブジェクトとして触らせず、メモリ内での操作を完結させるための強力なツールです。
// Web Crypto APIを使った鍵生成のイメージ
async function deriveKey(password, salt) {
const enc = new TextEncoder();
const keyMaterial = await crypto.subtle.importKey(
“raw”, enc.encode(password), “PBKDF2”, false, [“deriveKey”]
);
// 鍵は変数に保持せず、必要な操作が終わったら即座にスコープ外へ
return await crypto.subtle.deriveKey(
{ name: “PBKDF2”, salt, iterations: 100000, hash: “SHA-256” },
keyMaterial,
{ name: “AES-GCM”, length: 256 },
false, // 鍵をエクスポート不可にして盗難リスクを下げる
[“encrypt”, “decrypt”]
);
}
—
3. 「そもそも泥棒を入れない」ための防衛線
鍵の管理も大切ですが、泥棒(XSS)を入れないための「防衛ヘッダー」は、現代の開発において必須の防犯設備です。
Content-Security-Policy (CSP)
これは、ブラウザに対して「このサイトで実行していいスクリプトはこれだけだよ」と教えるホワイトリストです。
サーバーのレスポンスヘッダー設定例:
信頼できるドメイン以外のスクリプト実行を禁止する
Content-Security-Policy: default-src ‘self’; script-src ‘self’ https://trusted.cdn.com; object-src ‘none’;
default-src 'self': 基本的に自分のドメイン以外からの読み込みを拒否。script-src 'self': インラインスクリプト(HTMLに直書きされたJS)を禁止。これだけで多くのXSS攻撃は無力化されます。
HttpOnly Cookie
もしセッションIDなどの「鍵」をCookieに保存するなら、必ず HttpOnly 属性をつけましょう。これをつけると、JavaScriptからそのCookieを読み取ることができなくなります。泥棒が家の中に侵入しても、金庫の鍵だけは持ち出せないという仕組みです。
—
まとめ:セキュリティは「多層」で守る
ブラウザ上の暗号化は非常に便利ですが、「JavaScriptで動いている以上、クライアントサイドの暗号化は完璧な秘密保持にはならない」という前提を忘れないでください。
1. 鍵をlocalStorageに保存しない: XSSで一瞬で奪われます。
2. Web Crypto APIを活用する: 安全な暗号化アルゴリズムを使い、鍵をエクスポート不可(extractable: false)にする。
3. CSPで泥棒の侵入を防ぐ: 攻撃スクリプトが動かない環境を作ることが、暗号化よりも優先される防御策です。
セキュリティ対策に「これだけやれば絶対安心」という銀の弾丸はありません。ですが、泥棒が家に入れないようにドアを閉め、金庫の鍵を適切に管理し、いざという時のアラートを鳴らす。この泥臭い積み重ねこそが、皆さんのサービスを信頼あるものにしていきます。
一歩ずつ、セキュアな設計を一緒に作っていきましょう!
コメント