代表的な面接トピック

フロントエンド面接:エクスポート不可(non-extractable)な Web Crypto 鍵をどのように保存しますか?

フロントエンド難しい
Offer.cc 編集チーム公開日 更新日

質問

Web アプリケーションにおいて、XSS の脆弱性によってストレージから平文の鍵が簡単に読み取られないよう、ローカルの下書きデータを暗号化する必要があります。どのような設計を行いますか?また、その設計で保証できないことは何ですか?

プロンプトと設定

アプリケーションはブラウザ内のローカルデータを暗号化し、セッションをまたいで鍵を保持する必要があります。この設計では鍵の露出を減らす必要がありますが、オリジン内で実行されているコードは許可された操作を引き続き呼び出せることや、ユーザーが復元性を失う可能性があるという限界を明確にする必要があります。

面接官がテストするポイント

  • セキュアコンテキスト、CryptoKey のエクスポート可能性(extractability)、および鍵の用途(key usages)の理解。
  • 暗号化されたデータストレージと、鍵のリカバリおよび脅威モデリングの分離。
  • ブラウザ側の暗号化が XSS を無害化するわけではない理由の説明。

回答前に確認すべき明確化のための質問

  • 想定される脅威は、盗まれたストレージレコード、受動的攻撃者、それとも能動的な同一オリジンスクリプトですか?
  • データはログアウト、デバイスの紛失、パスワードのリセット、ブラウザプロファイルの削除後も維持される必要がありますか?
  • ユーザーが入力したパスフレーズや WebAuthn 認証情報でラッピングキーのロックを解除できますか?
  • どのアルゴリズム、鍵の用途、ブラウザサポート、移行パスが必要ですか?

30秒の回答フレームワーク

HTTPS を必須とし、extractable を false に設定して必要な用途のみを指定した CryptoKey を生成またはインポートし、生の鍵バイトをエクスポートするのではなく、鍵ハンドルを IndexedDB に保存します。暗号文、nonce、アルゴリズムパラメータ、およびバージョンは鍵とは別にして管理します。これに CSP、厳格な依存関係の制御、リカバリ計画を組み合わせますが、同一オリジンの能動的スクリプトはアプリが開いているときに暗号化 API を呼び出したり平文を読み取ったりできるという限界も明示します。

ステップバイステップの詳細解説

1. 脅威モデルの定義

エクスポート不可(non-extractable)にすることで、偶発的なエクスポートや多くのストレージ窃盗ルートを制限できます。ただし、オリジンでスクリプトを実行する攻撃者がアプリにデータの復号を要求したり、メモリ内の平文を読み取ったりすることを防ぐことはできません。セキュリティを約束する前に、その境界を述べる必要があります。

2. 鍵の作成またはインポート

サポートされているアルゴリズムと、encrypt や decrypt など目的に応じた特定の用途を使用します。セキュアコンテキストで鍵を生成するか、目的のエクスポート可能性フラグを指定してラップされたキーマテリアルをインポートします。予期しないアルゴリズムパラメータは拒否し、ローカルストレージに生のキーマテリアルを保存しないようにします。

3. 暗号文を安全に永続化する

暗号文、暗号化ごとに新しい nonce、認証済みメタデータ、鍵のバージョン、スキーマのバージョンを保存します。単一の鍵に対して一意の nonce を持つ AES-GCM などの認証付き暗号を使用します。暗黙の信頼決定とするのではなく、関連データ(associated data)内にテナントまたはユーザーのスコープを保持します。

4. リカバリとローテーションの計画

エクスポート不可な鍵は、プロファイルの削除やデバイスの紛失後に使用できなくなる可能性があります。平文へのサイレントなダウングレードを行うことなく、パスフレーズから導出されたラッピングキーやサーバーを介した再暗号化フローなど、計画的なリカバリパスを提供します。鍵のバージョン管理を行い、レコードをトランザクションとして移行します。

5. テストと運用

サポートされていないアルゴリズム、セキュアでないコンテキスト、クォータエラー、書き込みの中断、nonce の再利用、バージョンの移行、ログアウト、リカバリの失敗をテストします。CSP と依存関係のレビューを適用し、機密情報のログ記録を避け、平文を記録せずに復号の失敗を測定します。

質の高い回答例

「HTTPS を必須とし、extractable を false に設定した用途限定の CryptoKey を生成し、ブラウザが管理するそのハンドルのみを IndexedDB に永続化します。各レコードには、暗号文、一意の nonce、認証済みメタデータ、および鍵のバージョンを保存します。プロファイルの喪失によりエクスポート不可の鍵が復旧不能になる可能性があるため、リリース前にリカバリ方法を定義します。CSP と依存関係の制御によって能動的スクリプトのリスクを軽減しますが、オリジンで実行される XSS ペイロードは、アプリが開いている間に許可された復号操作を呼び出したり平文を読み取ったりする可能性があることも認識しておく必要があります。」

よくある間違い

  • 生の鍵を localStorage に保存する → ストレージの盗難により平文アクセスが露呈する → エクスポート不可な鍵ハンドルを永続化する。
  • AES-GCM で nonce を再利用する → 機密性と完全性が損なわれる可能性がある → 鍵およびレコードごとに一意の nonce を生成する。
  • XSS が解決されたと主張する → 同一オリジンのコードはアプリの権限を使用できる → 能動的スクリプトに対する限界を述べる。
  • リカバリ設計をスキップする → プロファイルの喪失によりデータへのアクセスが失われる → ラッピング、リセット、および移行フローを定義する。

フォローアップの質問と回答

IndexedDB を使用すれば、鍵は XSS から安全になりますか?

いいえ。単純なストレージ API に生のバイトを配置することは回避できますが、同一オリジンのスクリプトはデータベースにアクセスしたり、アプリケーションコードを呼び出したりすることができます。CSP、信頼できる依存関係、および現実的な脅威モデルを採用してください。

デバイスを紛失した場合、ユーザーはどのようにリカバリできますか?

ユーザーが保持するシークレットまたはリカバリ認証情報から導出されたキーでデータキーをラップするか、認証されたサーバーフローを介して再暗号化します。オフラインとリセットのトレードオフを説明し、古いデータを復号できない新しい鍵を暗黙的に作成することは避けてください。

ローテーション中に鍵をエクスポートすることはできますか?

ポリシーで許可されている場合のみ可能です。両方のバージョンが利用可能な間に新しい鍵を生成してレコードを再暗号化することを推奨します。ラッピングが必要な場合は、ラッピングキーを保護し、移行を監査してください。

公開情報ソース

関連する質問