ゼロ知識暗号化:Vaultedがあなたの秘密を守る仕組み
著者 Maxim Novak
ゼロ知識暗号化とは、サーバーがあなたの平文データや暗号化キーを決して見ないことを意味する。Vaultedは、データがサーバーに到達する前に、ブラウザ内でAES-256-GCMを使ってすべての秘密を暗号化することでこれを実現している。サーバーは暗号文のみを保存する — 復号キーへのアクセス手段は持たず、そのキーはURLフラグメントの中にのみ存在する。
この記事では、正確な暗号実装を解説する:キーの生成方法、データの暗号化方法、キーが受信者に届く仕組み、そしてパスフレーズを追加したときに何が起こるか。ここに記載されているすべての詳細は、あなたのブラウザで実際に動いているコードを反映している。
クライアントサイド暗号化が重要な理由についての概要は、クライアントサイド暗号化に関する以前の記事を参照してほしい。この記事では、暗号プリミティブをより深く掘り下げる。
キー生成
秘密を作成するたびに、VaultedはWeb Crypto APIを使って新鮮なAES-256-GCM暗号化キーを生成する:
crypto.subtle.generateKey({ name: 'AES-GCM', length: 256 }, true, [
'encrypt',
'decrypt',
]);
いくつか注意点がある:
- AES-GCM(Galois/Counter Mode)は機密性と完全性の両方を提供する — 不正読み取りだけでなく、改ざんも検出する。
- 256ビットキー長はAESで使用可能な最強のオプションであり、世界中の政府や金融機関が採用している。
- Extractable:
true— キーは生のバイト列としてエクスポートでき、受信者のためにURLフラグメントに埋め込むことができる。
Web Crypto APIはブラウザのネイティブ暗号エンジンに処理を委譲する(例:ChromiumではBoringSSL、FirefoxではNSS)。キーはメモリ内で安全に生成され、ディスクやネットワークには触れない。
暗号化
キーが生成されたら、Vaultedは平文を暗号化する:
const iv = crypto.getRandomValues(new Uint8Array(12));
crypto.subtle.encrypt(
{ name: 'AES-GCM', iv },
key,
new TextEncoder().encode(plaintext),
);
初期化ベクトル(IV): AES-GCMは各暗号化操作に対して12バイト(96ビット)のランダムなIVを必要とする。IVは秘密である必要はない — 暗号文とともに保存される — しかし、同じキーで再利用してはならない。Vaultedは秘密ごとに新しいキーを生成するため、IVの再利用は設計上不可能だ。
エンコード: 暗号文とIVはどちらもbase64url文字列としてエンコードされる(標準のbase64で+を-に、/を_に置換し、末尾の=を削除したもの)。これにより、URLやJSONペイロードへの組み込みが安全になる。
暗号化された暗号文とIVはサーバーに送信されて保存される。平文はブラウザの外に出ることはない。
URLフラグメントによるキー配送
暗号化後、キーはエクスポートされてURLフラグメント — #の後ろの部分 — に配置される:
const rawKey = await crypto.subtle.exportKey('raw', key);
// rawKey → base64url-encoded string
生成された共有リンクはこのような形になる:
https://vaulted.fyi/s/abc123#dGhpcyBpcyBhIGtleQ
^^^^^^^^^^^^^^^^^^^^^^^^
encryption key (base64url)
#区切り文字は重要だ。RFC 3986に従い、フラグメント識別子はクライアントによってのみ処理される。ブラウザはHTTPリクエストにフラグメントを含めない — URLにも、Refererヘッダーにも、サーバーが見られる場所には一切含まれない。
受信者がリンクを開くと:
- ブラウザはサーバーに
/s/abc123をリクエストする(フラグメントなし) - サーバーは暗号文とIVを返す
- JavaScriptが
window.location.hashからフラグメントを読み取る - キーはWeb Crypto APIに再インポートされる
- 暗号文はローカルで復号される
サーバーは暗号化されたblobの保存と取得を担う。暗号操作には一切関与しない。
オプションのパスフレーズ保護
追加の保護が必要な秘密のために、Vaultedはパスフレーズベースのキーラッピングをサポートしている。これにより第二の層が追加される:誰かがリンクを傍受したとしても、パスフレーズなしでは秘密を復号できない。
PBKDF2によるキー導出
パスフレーズを設定すると、Vaultedはこれを使ってPBKDF2でラッピングキーを導出する:
crypto.subtle.deriveKey(
{
name: 'PBKDF2',
salt, // 16 bytes, randomly generated
iterations: 600_000,
hash: 'SHA-256',
},
keyMaterial, // the passphrase, imported as raw key material
{ name: 'AES-KW', length: 256 },
false,
['wrapKey', 'unwrapKey'],
);
- 600,000回のイテレーションにより、ブルートフォース攻撃が計算上高コストになる。
- 16バイトのランダムソルトにより、同一のパスフレーズでも異なる秘密に対して異なる導出キーが生成される。
- SHA-256はPBKDF2の各イテレーションで使用されるハッシュ関数だ。
- 導出されたキーはAES-GCMキーではなくAES-KW(AES キーラップ、RFC 3394)キーだ。キーラッピングは暗号化とは別の操作だ。
暗号化キーのラッピング
導出されたAES-KWキーで元の暗号化キーをラップする:
crypto.subtle.wrapKey('raw', encryptionKey, wrappingKey, 'AES-KW');
URLフラグメントには、ラップされたキーとソルトの両方がドットで区切られて含まれる:
#wrappedKeyBase64url.saltBase64url
復号時のアンラッピング
受信者がパスフレーズを入力すると、Vaultedはプロセスを逆転させる:
- フラグメントを
.で分割し、ラップされたキーとソルトを取得する - パスフレーズとソルトを使ってPBKDF2で同じAES-KWラッピングキーを導出する
crypto.subtle.unwrapKeyで暗号化キーをアンラップする- アンラップされたAES-GCMキーで暗号文を復号する
パスフレーズが間違っている場合、unwrapKeyは例外をスローする — 部分的な復号や情報漏洩は起こらない。
復号フロー
パスフレーズなしの場合、復号は単純だ:
- URLフラグメントからキーをインポートする(
crypto.subtle.importKey) - IVを使って暗号文を復号する(
crypto.subtle.decrypt) - UTF-8バイト列から平文をデコードする
パスフレーズありの場合:
- フラグメントを分割し、ラップされたキーとソルトを抽出する
- パスフレーズとソルトからラッピングキーを導出する(PBKDF2)
- 暗号化キーをアンラップする(AES-KW)
- 暗号文を復号する(AES-GCM)
- 平文をデコードする
これらすべてはブラウザ内で行われる。サーバーは暗号化されたblobとメタデータ(閲覧回数、有効期限)を返すだけだ — それ以上でも以下でもない。
脅威モデル
どんなセキュリティツールもすべてから守ることはできない。ここでは、Vaultedのアーキテクチャが想定している脅威と、スコープ外の事項を示す。
Vaultedが守るもの
- サーバーへの侵害 — サーバーは暗号化された暗号文のみを保存する。キー(サーバーには決して送られない)がなければ、データは読めない。データベースの完全ダンプを取っても、暗号化されたblobが手に入るだけだ。
- データベース侵害 — 上記と同様。キーのない暗号文はAES-256-GCMにより計算上無意味だ。
- サーバー側の中間者攻撃 — 暗号化キーはURLフラグメントに存在し、HTTPリクエストで送信されることはない。TLSが転送中の暗号文を保護する。
- サービス運営者による秘密の閲覧 — ゼロ知識とはまさにそういう意味だ。キーを持っていないため、強制されたとしても私たちはあなたの秘密を読めない。
Vaultedが守らないもの
- ブラウザやデバイスへの侵害 — 受信者のデバイスにマルウェアやキーロガーが存在する場合、復号後に復号された平文が取得される可能性がある。
- 悪意のあるブラウザ拡張機能 — ページアクセス権を持つ拡張機能は、復号後のDOMを読み取れる。公開された秘密も含まれる。
- リンクの傍受 — URLにはフラグメント内に暗号化キーが含まれている。完全なURL(フラグメントを含む)を入手した者は誰でも秘密を復号できる。安全なチャネルでリンクを共有してほしい。
- のぞき見 — 秘密が画面に表示されたら、見ている人は誰でもそれを見ることができる。
- 受信者の行動 — 復号後、受信者はスクリーンショットを撮ったり、コピーしたり、平文を共有したりできる。Vaultedはこれを防ぐことはできない。
パスフレーズ機能はリンク傍受のリスクを軽減する:完全なURLがあっても、攻撃者にはさらにパスフレーズが必要だ。多層防御のため、別のチャネル(電話や別のメッセージ)でパスフレーズを共有してほしい。
まとめ
Vaultedのゼロ知識暗号化が意味するのは:
- 秘密ごとに新鮮なAES-256-GCMキーが生成される
- 暗号化はすべてブラウザ内で行われる
- サーバーは暗号化された暗号文のみを保存する
- 復号キーはURLフラグメントのみを通じて伝達され、ブラウザはサーバーにそれを送ることはない
- オプションのパスフレーズラッピングにより、PBKDF2とAES-KWを経由した第二の層が追加される
結果として:たとえすべてのサーバー、データベース、ネットワーク経路が同時に侵害されたとしても、あなたの秘密は暗号化されたままだ。キーはあなたが共有するリンクの中にのみ存在する。
安全に秘密を共有する準備はできた?Vaultedで秘密を作成する — 10秒でできる。
関連記事
- Vaultedのセキュリティ — セキュリティモデルの概要
- クライアントサイド暗号化が重要な理由 — ブラウザで暗号化することの根拠
- 暗号化プレイグラウンド — AES-256-GCM暗号化をステップごとに見る