Vaultedのzero-knowledge暗号化を自分で確認する
セキュリティツールを信頼するだけでなく — 検証しよう。4つのブラウザテスト、インストール不要、5分間。
60秒チェックリスト
まずこれをざっと確認しよう。下の詳細ウォークスルーで各ステップを詳しく説明している。
- 1
DevToolsを開く → Networkタブ
Vaultedで Cmd+Option+I(macOS)または F12(Windows/Linux)を押す。Networkタブに切り替えてFetch/XHRでフィルタリングする。
- 2
シークレットを作成してPOSTを確認
わかりやすいカナリア文字列(例: CANARY-12345)を入力してCreateをクリック。POST /api/secrets リクエストを見つける。Payloadタブを開くと、暗号文(base64)とivのみが表示される。ボディ内でカナリアを検索しても — 見つからない。
- 3
共有リンクを確認
生成されたリンクを見よう。復号キーは#(URLフラグメント)の後にある。RFC 3986に従い、ブラウザはフラグメントをサーバーに送らない — リクエストライン、ヘッダー、リファラーのいずれにも含まれない。
- 4
復号がクライアントサイドで実行されることを確認
新しいタブでリンクを開く。/api/secrets/[id]からのGETレスポンスには暗号文のみが含まれる。Sourcesパネルで crypto.subtle.decrypt を検索すると — その呼び出しはサーバーではなくブラウザ上で実行されている。
何を検証するか
Vaultedの主張は具体的だ: サーバーは平文も暗号化キーも見ることはない。 これは4つの観察可能な特性に分解できる:
- 平文はどのネットワークリクエストにも現れない。
- 暗号化キーはどのネットワークリクエストにも現れない。
- URLフラグメント(キーが存在する場所)はサーバーに届かない。
- サーバーサイドのデータだけではシークレットを再構築できない。
4つすべてが成り立てば、アーキテクチャは構造的にzero-knowledge — マーケティング用語は不要だ。
テスト 1 — 平文はネットワークを通過しない
- vaulted.fyi を開く。
- DevToolsを開いて Network タブに切り替える。
Fetch/XHRでフィルタリングする。 - シークレットフィールドにわかりやすい文字列を入力する — 例えば
CANARY-12345。 - Create をクリックする。
単一の POST リクエストが /api/secrets に表示される。それをクリックして Payload タブを確認する。
ボディには ciphertext フィールドと iv フィールドが含まれている。どちらもbase64urlエンコードされたblobだ。ペイロードで CANARY-12345 を検索しても — 見つからない。平文はリクエストが送信される前にブラウザ内で暗号化された。
念を入れたい場合は、ネットワークスロットリングを「Slow 3G」に設定して繰り返す。暗号文は引き続きノイズのように見え、平文はどこにも見当たらない。
テスト 2 — キーもネットワークを通過しない
シークレットを作成すると、Vaultedは共有リンクを表示する。こんな形だ:
https://vaulted.fyi/s/abc123#dGhpcyBpcyBhIGtleQ
^^^^^^^^^^^^^^^^^^^^
encryption key (base64url)# の後の部分がURLフラグメントだ。RFC 3986 に従い、ブラウザはフラグメントをローカルで処理する — HTTPリクエストライン、ヘッダー、リファラーには現れない。
これを証明するには:
- 共有リンクをコピーする。
- DevToolsのNetworkタブを開いた状態で新しいタブで開く。
GET /s/abc123リクエストを見つける。- Headers タブを確認する — 完全なリクエストURL、フラグメントなし。
- Request タブを確認する — フラグメントなし。
- 次のページで
document.referrerを確認する — フラグメントは除去されている。
受信者がシークレットを復号するために必要なキーはURLの中にあったが、そのブラウザ内に留まった。サーバーが見たのは GET /s/abc123 のみだ。
テスト 3 — サーバー単独では復号できない
ターミナルを開いて、curl でシークレットを直接取得する:
curl https://vaulted.fyi/api/secrets/abc123ciphertext と iv を含むJSONが返ってくる。これがシークレットのサーバーサイドの完全な状態だ。
これを解読しようとしてみよう。URLフラグメントのキーなしでは無理だ。適切に生成されたランダムキーによるAES-256-GCMは、実用的な観点からランダムノイズと区別できない。Vaultedのサーバーからの完全なデータベースダンプでも、得られるのは暗号化されたblobとメタデータだけだ。
これがzero-knowledgeの文字通りの定義だ:サーバーは読めないデータを保存している。
テスト 4 — 暗号化は見えるJavaScriptで実行される
Vaultedは Web Crypto API を使っている。これはブラウザのプリミティブで、難読化されたWASMやネイティブモジュールで処理を隠してはいない。暗号化が行われる様子を直接観察できる。
- DevToolsで Sources タブを開く。
crypto.subtle.encryptを検索する(グローバル検索にはCmd+Option+Fを使う)。crypto.subtle.encryptを呼び出す行にブレークポイントを設定する。- 新しいシークレットの作成をトリガーする。
- ブレークポイントが当たる。引数を確認すると:平文が
Uint8Arrayとして、AES-GCMキー、フレッシュなランダムIVが見える。 - 呼び出しをステップオーバーする。結果が送信される暗号文だ。
ネットワークリクエストの前に、ブラウザ内で平文が暗号文になる様子をリアルタイムで観察している。データが通る他の経路はない。
これが証明すること
- 平文はブラウザの外に出ない。 テスト1。
- キーはブラウザの外に出ない。 テスト2。
- サーバーは保存しているデータで復号できない。 テスト3。
- 暗号化は本物でクライアントサイドで実行される。 テスト4。
これがzero-knowledgeの主張の全体であり、5分間で稼働中の本番システムに対して実証された。
これが証明しないこと
検証は誠実な作業だ — その限界について明確にしておく価値がある。
- テストはこの瞬間の動作を証明する。 将来のコード変更がこの特性を破る可能性がある。継続的な保証が必要な場合は、changelog を購読して定期的にテストを再実行しよう。
- デバイス自体に対しては保護できない。 キーロガーや悪意のあるブラウザ拡張機能は、復号後に平文を読むことができる。これはどのWebアプリの制御範囲外だ。
- 実行したJavaScriptがVaultedが配信するJavaScriptであることを前提としている。 CDNを侵害した標的型攻撃者は、特定ユーザーに異なるバンドルを配信できる可能性がある。Subresource Integrityと再現可能ビルドでこれを軽減できる。詳細は 脅威モデル を参照。
さらに深く掘り下げる
より詳しく調べたい場合:
- zero-knowledge暗号化の詳細解説 を読んで完全な暗号実装を確認しよう。
- Encryption Playground を試して AES-256-GCM をステップバイステップで観察しよう。
- サーバーサイド暗号化 と比較して、どちらのモデルが自分の脅威モデルに合うか判断しよう。
セキュリティツールに対する最も強力な論拠はそのマーケティングではない。分解して、主張通りの動作をすることを確認できることだ。Vaultedはそれができるように設計されている。