「自分がいなくてもVaultを止めるな」— セキュリティと可用性の板挟みで深夜に唸った設計格闘記
前回のあらすじ
「次回はバイナリ軽量化」と予告しておきながら、先にこっちを書きます。
前回の記事では、GoのゲートウェイがHTTPリクエストを受け取って、FFI経由でRustのエンジンを叩いて、結果が返ってくるまでを確認しました。
[1/2] Checking API Health... [SUCCESS]
[2/2] Testing Rust Engine... [SUCCESS]
{"ppid": "PPID-PREXUS-akiny-victory-2026-final"}
画面を見つめながら、しばらく動けなかった話を書きました。
でも、SUCCESSが出た翌朝、新しい問いが生まれていました。
「動いた。でも、俺が操作できなくなったら、このシステムはどうなる?」
この記事はその問いと格闘した記録です。
今回の戦場
技術的なバグとの格闘ではなく、設計思想の選択との格闘です。
Prexusが目指しているのは、「医療データの真正性・秘匿性・耐障害性を三位一体で保証する」こと。この三つは、ある局面で激しくぶつかり合います。
- 秘匿性を極めると、鍵を持つ人間がいないとデータが読めなくなる
- 耐障害性を極めると、誰かに依存しない自律稼働が必要になる
「誰も覗けないシステム」と「誰がいなくても動くシステム」は、根本的に矛盾する。
その矛盾と、深夜に何時間も向き合いました。
第1ラウンド:パスワードなし構成のリスク
最初の問いはシンプルでした。
「Vaultのシステム稼働用コンポーネントにパスワードを設定しないのは、リスクが高いか?」
Prexus Sentinel v4.0の認証設計はこうなっています。
AES_Key = SHA256(物理キー + パスワード)
物理USBキーと知識(パスワード)の組み合わせで鍵を導出する二要素構成です。これが「真正性の極致」という設計思想の核心です。
ここで「システム用(常時稼働)」と「管理モード(人間が操作する)」を分けて、システム用にはパスワードを設定しない構成を考えました。
自動化のためには合理的に見えます。でも問題がありました。
問題A:数学的証明の連鎖が途切れる
システム用ノードがパスワードなし(=固定鍵)で動いている場合、そのノードのデータは「バイナリを解析すれば復号できる」状態になります。「管理者ですら中身を覗けない」という保証が、システム用ノードだけで崩れてしまう。
問題B:踏み台にされるリスク
管理モードに強固なパスワードがあっても、システム側が「裸」であれば、攻撃者はシステム側を突破してDBを操作する道を探ります。強い扉の隣に無施錠の窓を作るようなものです。
セキュリティを「最も弱いところ」で評価するなら、システム用の設計が全体の強度を決める。
結論:パスワードなし構成は採用しない。ただし、「自動稼働」という要件は捨てない。別の解を探す。
第2ラウンド:「自分が長期不在になっても、Vaultを止めたくない」
問いが深まりました。
「俺が何らかの事情でパスワードを入力できなくなった場合でも、Vaultは動いていてほしい。」
これは防災士としての発想です。インフラは、管理者個人の状態に依存してはいけない。
この要件と「二要素認証」は正面衝突します。人間の入力(パスワード)を必要とする設計は、その人間が不在だとシステムが止まる。
3つの方向性を検討しました。
案1:「運用継続用」システム専用ノードの分離
人間の「知識(パスワード)」に頼らず、「特定のサーバー環境が持つ秘密情報(TPMチップ)」と「物理USBキー」の組み合わせで鍵を生成する。
メリット:管理者不在でも、サーバーが生きていて物理キーが刺さっていれば自律稼働できる。
デメリット:サーバーごと盗まれた場合、パスワードという壁がない。
案2:デッドマンズ・スイッチ
管理者が一定期間アクセスしなかった場合に、自動でバックアップキーを有効化する。「30日間ログインがない場合は緊急モードへ移行」のような仕組みです。
案3:シャミアの秘密分散法
マスターパスワードを「3つの破片」に分割する(管理者・信頼できるパートナー・サーバー内の安全な領域)。普段は「管理者+サーバー」の2つで動かし、管理者不在時は「パートナー+サーバー」で継続稼働させる。
数学的な解決策というのが、Prexusらしい。
ただしどの案も、「可用性を確保する=どこかでセキュリティを妥協する」という構造は変わりません。
この時点での設計決断:
システム用ノードに持たせる権限を「書き込みのみ(Append Only)」に制限する。過去データの復号・閲覧権限は剥奪する。
これなら、システム側が突破されても過去の機密データは守られる。管理者が戻ってきた時に初めて、溜まったデータを安全に読み出せる。「Vaultが止まらない」と「誰にも覗かれない」を完全には両立できないが、被害の上限を設計で制御できる。
第3ラウンド:現場でUSBキーが物理的に邪魔
設計の議論が進んだところで、まったく別のレイヤーの問題が出てきました。
「現場でタブレットを使って作業する時、USBが刺さっていると邪魔だし、ぶつけて破損させる可能性が高い。タブレットは手入力のパスワードだけにしたい。」
これは現場の話です。トラックの運転中や屋外作業中に、タブレットからUSBキーがピョコッと飛び出している状態は現実的ではない。ポートごと壊れたら、その場でシステムが止まります。
「物理キー必須」という設計と「現場の機動力」がぶつかった。
解決の方向性は「場所によって認証レベルを分ける」です。
司令塔(サーバー): 物理キー+パスワード で稼働
現場(タブレット): パスワードのみ で通信
タブレット側にはマスターキーを持たせない。サーバーから発行された「時限式セッショントークン」で通信する。
万が一タブレットを紛失しても、サーバー側でそのトークンを無効化すれば即座にアクセスを遮断できます。物理キーそのものを盗まれるより、はるかに被害を限定できる。
第4ラウンド:「朝の儀式」と「夜の儀式」という設計
現場の問題を整理しているうちに、一つの運用サイクルが浮かび上がりました。
「朝と一日の終わりに必ずUSBを挿すルーチンにして、朝はパスワードを保管、夜はデータの保管とパスワードの破壊にしたら?」
これが今回の設計で一番しっくり来た発想でした。
朝の儀式:Volatile Key Injection(揮発性鍵の注入)
出勤時に物理USBキーを接続し、パスワードを入力する。
// AES_Key を生成し、メモリ上(RAM)にのみ保持
pub struct VaultCore {
master_key: [u8; 32], // ストレージには書かない
is_active: bool,
}
ポイントは「ストレージに保存しない」ことです。メモリ上にのみ保持することで、電源を切ったり物理的に持ち去られたりしても、鍵は取り出せない。
日中:Session Persistence(セッションの継続)
USBキーを抜いて、安全な場所(金庫や自宅)に保管して現場へ。タブレットはパスワード入力だけでVaultと通信。Vault本体は朝注入された「メモリ上の鍵」を使ってリアルタイムで動き続けます。
夜の儀式:Memory Wipe & Cold Storage(メモリ消去とコールドストレージ化)
一日の終わりに再びUSBキーを接続し、その日のセッションを閉じます。
処理の流れ:
- その日の最終ハッシュを確定し、ログをバックアップ
- メモリ上の
AES_Keyを完全に上書き消去(破壊) - システムを「完全閉鎖」状態に戻す
物理キーとパスワードが揃わない限り、誰一人として中身を覗けない状態(Cold Storage)で夜を越す。
朝に魂を吹き込んで、夜に眠りにつかせる。
なんか日本的な感じがして、この設計が気に入っています。
停電への対策:
日中にサーバーが落ちた場合、メモリ上の鍵は消えます。その場合は再認証(USB挿入)が必要になりますが、これは「不正な持ち出し」を防ぐフェイルセーフとして機能します。止まることが、守ることになる。
第5ラウンド:使い捨てIDと操作ログの両立
最後に出てきたのが「誰が何時に何をしたか」の記録という問いでした。
「朝保管するパスワードはマスターキーでシステムを裏で起動して、管理画面を開く処理は敢えて簡単なIDで入れるんだけど、管理画面を閉じるたびにIDを破壊して、誰が何時にどんな処理をしたかも管理できるようにするとか?」
これが「ゼロトラスト」の考え方そのものです。
二重構造の起動シーケンス
バックエンド(エンジンの火入れ)
└── 物理キー+マスターパスワードで起動
└── DBへのパスが「導通」状態になる
フロントエンド(現場用IDの発行)
└── 管理画面を開くたびに「その瞬間だけのセッションID」を生成
└── マスターパスワードで署名された暗号トークンに変換
└── 操作権限を付与
入力は「簡単なID」でいい。システム内部でそれを「マスターキーで署名された暗号トークン」に変換するので、IDが単純でも外部からの不正アクセスは弾かれます。
作業ごとのID破壊と非可逆ログ
管理画面を閉じるたびに、そのセッションIDをメモリ上から抹消します。同時に、そのセッションで行われた全操作をハッシュチェーンに刻む。
impl VaultCore {
// セッション終了(ID破壊と証跡記録)
pub fn destroy_session(&self, session: WorkSession) {
let duration = now() - session.start_time;
// 誰が何をしたかをハッシュチェーンへ完全固定
log_to_hash_chain(
"SESSION_END",
&format!("ID: {}, Duration: {}sec", session.session_id, duration)
);
// メモリ上のセッション情報をゼロクリア
drop(session);
}
}
「Aさんが10:15にログイン→10:20にデータ入力→10:21にログアウト(ID破壊)」という一連の流れが、ハッシュの鎖の一ピースとして刻まれます。あとから「その操作はなかったことにしよう」としても、チェーンが繋がっているため改ざんは不可能です。
記録を残すのではなく、記録を消せない構造にする。
この発想の転換が、今回の設計で一番大きかったかもしれません。
今回の設計決断まとめ
□ システム用ノードの権限を「書き込みのみ」に制限する
□ システム用には「サーバー環境+物理キー」で鍵を導出し、パスワードに依存しない
□ タブレット(現場)は時限式セッショントークンで通信、マスターキーは持たせない
□ 鍵はメモリ上にのみ保持し、ストレージには書かない(揮発性設計)
□ 朝の火入れ・夜のメモリ消去を運用ルーティンとして設計に組み込む
□ 管理画面のセッションIDは閉じるたびに破壊し、操作証跡をハッシュチェーンへ刻む
□ 停電・再起動時のメモリ消去は「フェイルセーフ」として肯定的に捉える
おわりに
今回はエラーメッセージが一つも出てきませんでした。格闘したのはコードではなく、「何を守るために何を諦めるか」という問いでした。
セキュリティと可用性は、どこかで必ず妥協点を探すことになります。「完璧に安全で、完璧に止まらない」システムは存在しない。設計とは、そのトレードオフをどこで引くかを決める作業です。
今回の結論は「被害の上限を設計で制御する」でした。完全な安全は無理でも、最悪のシナリオを「書き込まれた過去データが読まれる」ではなく「新規書き込みが止まる」に限定できれば、医療インフラとして許容できる。
深夜に一人、こういう問いと格闘するのも、悪くないです。
次は Prexus Gatewayのセッション管理と証跡設計の具体的な実装 に入る予定です。今回の設計思想をどうコードに落とすか、また格闘記録を書きます。
PrexusのGateway部分はオープンソース化予定です。公開のタイミングはXかZennでお知らせします。
Vault内部の暗号化ロジック・鍵管理の実装詳細については、セキュリティ上の理由から公開していません。
Discussion