代表的な面接トピック

システム設計面接:マルチテナント対応シークレット管理サービスの設計

システム設計難しい
Offer.cc 編集チーム公開日 更新日

質問

2,000万個のアクティブなシークレット名を保存し、ピーク時に毎秒100,000件の読み取りと2,000件の書き込みを処理し、リージョン内の読み取りにおいてp99で50ミリ秒未満を返すマルチテナント対応シークレット管理サービスを設計してください。ワークロードは最小権限のアイデンティティを使用し、確認応答(ACK)済みの書き込みはリージョン内で失われてはならず、通常のストレージが平文のシークレット値を受け取ることは決してあってはなりません。バージョン管理、ローテーション、失効、監査、動的シークレットのリース、およびリージョン障害からのリカバリをサポートしてください。脅威モデル、API、データモデル、暗号化境界、整合性、キャッシング、可用性、ディザスタリカバリ、および検証について説明してください。

プロンプトと適用可能なコンテキスト

マルチテナントプラットフォーム向けのシークレット管理サービスを設計します。シークレットには、データベースの認証情報、APIトークン、秘密鍵、証明書などが含まれます。システムは2,000万個のアクティブなシークレット名を保存し、平均して3つのバージョンを保持します。シークレット値の平均サイズは2 KiBで、最大64 KiBです。ピーク時のトラフィックは毎秒100,000件の読み取りと2,000件のバージョン書き込みです。シークレットのホームリージョンにおいて、成功した読み取りはp99で50ミリ秒未満で完了する必要があります。

各リージョンは3つのアベイラビリティゾーン(AZ)にまたがっています。ホームリージョンで確認応答(ACK)された書き込みは、1つのゾーンの喪失に耐えなければなりません。リージョン全体が失われた場合のディザスタリカバリ(DR)目標は、RTO 15分、RPO 1分です。これらのデータ量と目標は面接用の前提条件であり、商用シークレット製品の公表された制限値ではありません。ビジネス要件としてリージョン障害後のデータ損失ゼロ(RPO 0)が求められる場合、候補者は明示的に同期的なクロスリージョン合意やその他の同等の耐久性境界を採用する必要があります。

脅威モデルには、データベーススナップショットの盗難、バックアップの侵害、テナント間の認可ミス、通常ストレージへのアクセス権を持つオペレーター、誤ったログ出力、および1台のデータプレーンホストの侵害が含まれます。正当に取得した値をすでに認可されたワークロードが悪用することを完全に防ぐことは対象外です。本設計では、そのワークロードの権限と認証情報の有効期間を削減し、帰属性(属性追跡)を維持し、侵害時の影響範囲(ブラストレイディウス)を制限する必要があります。

スコープには、ワークロード認証、ポリシー評価、静的および動的シークレット、暗号化、イミュータブルなバージョン、段階的ローテーション、失効、リース、監査、レプリケーション、およびリカバリが含まれます。組織のアイデンティティプロバイダー(IdP)、生成された認証情報を使用するデータベース、汎用的な暗号化キー管理サービス(KMS)の構築は外部依存関係となります。2026年時点で公開されているセキュリティエンジニアリングの面接資料には、シークレット管理サービスの設計という具体的なプロンプトが含まれており、セキュリティシステム設計の演習としてマイクロサービス向けシークレットマネージャーが挙げられています。これは特定の企業における頻出度を証明するものではありませんが、本トピックが現代の設計課題を代表していることを示しています。

面接官が評価するポイント

第1の評価ポイントは、正確な保護境界です。「データベースを暗号化する」と答えるだけでは、復号鍵が暗号文の隣に置かれたままになり、どの侵害が封じ込められるのかが説明されていません。優れた回答では、ハードウェアまたはKMSで保護されたルート、テナントスコープのキー暗号化キー(KEK)、バージョンごとのデータ暗号化キー(DEK)、および通常の暗号文ストレージを明確に分離します。また、平文が一時的にどこに存在し得るかも特定します。

第2の評価ポイントは、すべてのリクエストにおける認可です。テナントの分離は、呼び出し元が提供するパスプレフィックスに依存してはなりません。サービスは、認証されたアイデンティティからテナント、ワークロード、環境、ロールを導出し、特定のアクションとリソースに対してバージョン管理されたポリシーを評価し、それらの事実を監査イベントに含めます。認証、認可、暗号化、および監査はそれぞれ独立したコントロールです。

第3の評価ポイントは、ライフサイクルの論理的整合性です。新しい値の作成、暗号化ラッピングキーの変更、外部データベースにおける認証情報の変更は、それぞれ異なる3つの操作です。外部システムをまたぐローテーションは、単一のアトミックなデータベーストランザクションではありません。耐久性のあるステージ、重複許容(オーバーラップ)ルール、冪等性、テスト、補償処理、および調整(リコンシリエーション)が必要です。

第4の評価ポイントは、現実的な失効セマンティクスです。将来の読み取りを拒否しても、プロセス内にすでにコピーされた静的パスワードが消去されるわけではありません。効果的な緊急失効を行うには、その認証情報を受け入れるシステム側でも無効化またはローテーションを実行する必要があります。動的認証情報の場合、ターゲットシステムがリースに参加するため、より強力な有効期限管理と失効を提供できます。

最後に、候補者はレイテンシ、セキュリティ、および可用性のバランスを取る必要があります。毎秒100,000件の読み取りごとにHSMを呼び出すのは、通常、誤ったボトルネックの作り方です。共有の分散キャッシュに平文をキャッシュするのは誤った解決策です。回答では、キー階層、スコープを絞った保護メモリキャッシュ、明示的なステールポリシー制限、および検証済みの障害モードを用いる必要があります。

回答前に確認すべき質問

  • どのシークレットタイプがスコープに含まれるか? 不透明な(Opaque)静的値にはバージョンストレージが必要です。データベースユーザーや証明書については、動的発行およびターゲット側での失効もサポートする場合があります。
  • 誰がシークレットを読み取るのか? ワークロードアイデンティティと短命なセッションを優先します。人間の閲覧を許可する場合でも、より強力な承認、ステップアップ認証、および個別の監査ポリシーが必要です。
  • 失効は何を保証するのか? サービスは新しい読み取りを即座に拒否できます。コピー済みの静的値を無効化するには、ダウンストリームシステム側でローテーションまたは無効化を行う必要があります。
  • current にはどのような整合性が必要か? 本設計では、シークレットごとの昇格および失効をホームリージョン内で線形化可能(Linearizable)にします。呼び出し元はイミュータブルなバージョンを明示的に要求することもできます。
  • リージョン障害時にデータ損失ゼロ(RPO 0)を許容する必要があるか? 提示されたDR RPOは1分です。RPO 0を達成するには同期的なクロスリージョン確認応答が必要となり、レイテンシと可用性のトレードオフが変化します。
  • 障害時にアプリケーションはローカルキャッシュを使用できるか? 明示的なポリシーが許可している場合に限り、特定の静的シークレットに対して暗号化された時間制限付きのエージェントキャッシュを許可できます。ただし、これにより即時失効の保証は弱まります。
  • 値とバージョンのサイズはどの程度か? プロンプトでは値を最大64 KiBとし、平均3つのバージョンを保持すると仮定しています。巨大なドキュメントは、別の暗号化オブジェクトストレージ設計で扱うべきです。
  • 監査レコードには何を含める必要があるか? プリンシパル、ワークロード、テナント、リソース識別子、アクション、ポリシーバージョン、判定結果、リクエストID、リージョン、タイムスタンプを含めます。平文の値は絶対に含めません。
  • 誰がトラストルート(信頼の基点)を管理できるか? 職務分掌、クォーラム制御によるブレークグラス(緊急アクセス)手順、およびリカバリ訓練が必要です。万能な日常的管理者の存在はテナント分離を無効化します。

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

「ポリシーとオーサリングを担うコントロールプレーンと、低レイテンシな読み取りデータプレーンを分離します。ワークロードは短命なプラットフォームアイデンティティで認証し、サービスはそのアイデンティティからテナントを導出し、シークレットとアクションに対してバージョン管理された最小権限ポリシーを評価します。イミュータブルな各シークレットバージョンは、認証付き暗号(AEAD)を使用してランダムなデータキーで暗号化されます。そのデータキーはテナントキーでラップされ、テナントキーをアンラップするルートはHSMまたはKMSによって保護されます。通常のストレージには暗号文とラップされたキーのみが保存されます。リージョン内のトランザクショナルクォーラムがアトミックにバージョンを追加し、Compare-and-Setを用いて current ポインタを移動します。外部ターゲットは単一トランザクションに参加できないため、ローテーションは作成、インストール、テスト、昇格、オーバーラップ、退役という耐久性のあるワークフローとして実行します。動的認証情報には、発行者が更新または失効させるリースが付与されます。暗号文はキャッシュしますが、アンラップされたキーは強化されたサービスメモリ内のみに短時間キャッシュし、平文を共有キャッシュに置くことは決してありません。監査ログは独立して保護された追記専用パイプラインに送信します。テナント間アクセスの拒否、並行昇格、キーの喪失、KMS障害、失効のスタック、ログ漏洩、および完全なリージョンリストアをテストして検証します。」

ステップごとの詳細解説

ステップ1:プロンプトを不変条件と脅威境界に落とし込む

まず5つの不変条件を定義します。

text
1. A request can address only a tenant derived from authenticated identity.
2. Ordinary databases, queues, logs, traces, and backups never receive plaintext values or plaintext keys.
3. A published version is immutable; promotion only changes a version pointer.
4. Every allow and deny decision emits an audit event without the value.
5. Acknowledged regional writes survive one availability-zone failure.

これらの不変条件を設定しても、残留リスクは残ります。認可されたワークロードは平文を受け取るため、そのワークロード自体が侵害される可能性があります。データプレーンプロセスはアンラップされたキー素材と平文を一時的に保持します。悪意のあるトラストルート管理者が通常のポリシーを超える操作を行う可能性もあります。これらのリスクを低減するために、短命なアイデンティティ、厳格なポリシー、プロセス分離、コアダンプの無効化、保護メモリ、独立した監査、職務分掌、およびキーの適用範囲の最小化を実施します。保存時の暗号化(Encryption at Rest)だけで実行時の侵害を解決できると主張してはなりません。

ステップ2:コントロールプレーンと読み取りデータプレーンの分離

コントロールプレーンは、テナント、ポリシー、シークレットのメタデータ、新しいバージョン、ローテーションジョブ、承認、および緊急アクションを管理します。書き込み頻度は低く、厳密な検証と監査可能なワークフローが重視されます。データプレーンは、ワークロードを認証し、ポリシーを評価し、許可されたバージョンを復号して、相互認証TLS(mTLS)経由で返します。そのホットパスは最小限に保つ必要があります。

各テナントには、シークレットとポリシーの確定的な書き込みを行うホームリージョンが存在します。そのリージョン内の3ゾーンにまたがるトランザクショナルクォーラムが、メタデータ、暗号文、バージョンポインタ、リース、冪等性レコード、および監査アウトボックスをコミットします。読み取りノードは水平スケール可能ですが、current の読み取りはホームリージョンの強整合性ストアまたは読み取りバリアが実証されたレプリカを経由します。古いレプリカが失効済みのバージョンを復活させてはなりません。

アイデンティティは、ワークロード証明書や署名付きサービスアカウントトークンなどのメカニズムを介して、プラットフォームのIdPから提供されます。シークレットサービスは、発行者、オーディエンス、有効期限、ワークロードアイデンティティ、チャネルバインディングを検証し、信頼できるアイデンティティをテナントとロールにマッピングします。クライアントがヘッダーを使用してテナントを上書きすることはできません。

ステップ3:軽量なAPIとイミュータブルなデータモデルの定義

API設計の例を以下に示します。

text
POST /v1/secrets/{path}/versions
  { value, idempotencyKey, expectedCurrentVersion? }
  -> { secretId, version, status: "PENDING" }

POST /v1/secrets/{path}/versions/{version}/promote
  { expectedCurrentVersion, idempotencyKey }
  -> { currentVersion }

GET /v1/secrets/{path}?version=current
  -> { value, version, expiresAt? }

POST /v1/dynamic/{role}/credentials
  { requestedTtl, idempotencyKey }
  -> { value, leaseId, expiresAt, renewable }

POST /v1/leases/{leaseId}/renew
POST /v1/leases/{leaseId}/revoke

パスはリソース名であり、認可の証明ではありません。サーバーはパスを一度正規化し、あいまいなエンコーディングを拒否し、アイデンティティからテナントを導出して、アクション固有のポリシーをチェックします。

text
Secret(secret_id, tenant_id, canonical_path, state,
       current_version, policy_ref, created_at)

SecretVersion(secret_id, version, status, ciphertext, nonce, auth_tag,
              wrapped_dek, tenant_kek_version, content_fingerprint_hmac,
              created_by, created_at)

Policy(policy_id, tenant_id, version, document, state, created_at)

Lease(lease_id, tenant_id, secret_id, principal_id, target_ref,
      status, expires_at, renewable_until, last_error)

Idempotency(tenant_id, principal_id, operation, key,
            request_fingerprint_hmac, result_ref, expires_at)

(tenant_id, canonical_path) および (secret_id, version) は一意です。バージョンのレコードは、暗号化されたペイロードを変更することはありません。昇格には current_version に対する条件付き更新を使用します。2人の管理者がバージョン7からの昇格で競合した場合でも、互いの変更を警告なしに上書きすることはできません。監査アウトボックスは各状態変更とともにコミットされ、後に個別に管理されたストレージにエクスポートされます。フィンガープリントには、テナントごとにスコープされた個別のキーで作成されたキー付きHMACを使用し、盗まれたスナップショットからオフラインで推測される恐れのある低エントロピーな認証情報の単純なハッシュは使用しません。

ステップ4:エンベロープ暗号化階層の構築

すべてのバージョンに対してランダムなデータ暗号化キー(DEK)を生成し、認証付き暗号(AEAD)モードで値を暗号化します。tenant_idsecret_id、バージョン、およびアルゴリズム識別子を追加認証データ(AAD)としてバインドし、暗号文が検出されずに別のテナントやバージョンに移動されないようにします。暗号文、ナンス、認証タグ、およびラップされたデータキーを一緒に保存します。

各テナントは、1つ以上のバージョン管理されたキー暗号化キー(KEK)を持ちます。HSMまたはクラウドKMSによって保護されたルートは、認可されたデータプレーンノードに対してのみテナントキーをアンラップします。テナントキーは各バージョンのデータキーをラップします。したがって、通常のストレージスナップショットには値を復号するために必要なキー素材が含まれていません。データプレーンホストが1台盗難された場合でも、そのプロセス内に存在するテナントキーとデータキーのみが危険にさらされるため、配置制御とキャッシュスコープによって1つのノードが処理できるテナント数を制限する必要があります。

text
HSM/KMS root
  -> unwrap tenant KEK version
      -> unwrap per-secret-version DEK
          -> AEAD-decrypt secret value with bound tenant/version metadata

読み取りごとにHSMを呼び出すと、ルートサービスがレイテンシとスループットのボトルネックになります。データプレーンノードは、テナントのリスクに応じてパーティション分割された強化メモリ内に、アンラップされたテナントキーまたはデータキーを短期間のみキャッシュできます。ポリシーやキーのイベントが発生した際、およびプロセスのシャットダウン時にはキャッシュをクリアします。共有Redis、ディスクスワップ、クラッシュダンプ、ログ、トレースが平文やアンラップされたキーを受け取ることは決してありません。

テナントのラッピングキーを変更する場合、保存されているすべての値を復号することなく、データキーを再ラップ(Re-wrap)できます。データベースのパスワードを変更する場合は、新しいシークレット値が作成され、データベースとの調整が必要になります。これらの操作は影響範囲が異なるため、ひとまとめの曖昧な「ローテーション」ボタンを共有してはなりません。

ステップ5:認可とキャッシングを失効対応にする

ポリシーでは、create-version、promote、read、issue-dynamic、renew、revoke、administer-policy などのアクションを指定します。テナント、プロジェクト、環境、パス、ワークロード、ネットワークゾーン、時間、および最大TTLを制限できます。明示的な拒否(Deny)は許可(Allow)に優先します。人間による平文アクセスはデフォルトで無効化されており、承認された例外措置にはチケット、承認者、理由、および短期間の許可レコードが記録されます。

低レイテンシを維持するためにポリシー評価はローカルで実行されますが、キャッシュされたポリシーにはバージョンと最大有効期間が設定されます。ポリシーまたはプリンシパルの失効が発生した場合は、まずコミットされ、無効化通知が発行され、テナントの認可エポックが進められます。読み取りノードは、リクエストを処理する前に少なくとも必要なエポックを保持していることを証明しなければなりません。無効化通知が利用できず最大有効期間が切れた場合、機密性の高い読み取りはポリシーを信用し続けるのではなく、安全側に倒して失敗(Fail-closed)させます。

暗号文とイミュータブルなメタデータは、広くキャッシュしても安全です。平文は安全ではありません。ワークロード側のエージェントは、許可された値を設定されたTTLまで自身のメモリに保持することで、繰り返しの読み取りを減らすことができます。可用性が極めて重要な特定のシークレットに対して製品が暗号化されたディスクキャッシュを許可する場合、デバイスにバインドされたキー、有効期限、および弱まる失効保証を明示する必要があります。コントロールプレーンがダウンしているからという理由だけで古いシークレットを返すことは、普遍的なフォールバック策にはなり得ません。

ステップ6:認証情報ローテーションを復元可能なワークフローとして扱う

ターゲットデータベースにまたがる静的認証情報のローテーションは、耐久性のあるステージに従って実行されます。

text
CREATED_PENDING
  -> INSTALLED_AT_TARGET
  -> VERIFIED
  -> PROMOTED_CURRENT
  -> OLD_VERSION_IN_OVERLAP
  -> OLD_VERSION_REVOKED
  -> COMPLETE

各状態遷移には、冪等なコネクタ操作と記録された証跡が存在します。新しい認証情報を作成し、ターゲットにインストールし、意図したパス経由でテストし、新しいバージョンを昇格させ、コンシューマーが必要とする場合は制限されたオーバーラップ期間を許可し、その後ターゲット側で古い認証情報を無効化します。リコンシリエーションワーカーがワークフローの状態とターゲットの状態を比較し、クラッシュ後に処理を再開します。インストールに成功したものの応答が失われた場合、別の認証情報を再作成するのではなく、照会処理によってその事実を発見する必要があります。

緊急ローテーションでは、古い値が侵害された疑いがあるため、オーバーラップ期間をスキップする場合があります。この選択はアプリケーションの停止を引き起こす可能性があるため、インシデント専用の認可パスを必要とします。ターゲットシステムが古い認証情報をまだ受け入れている間は、保存されている古いバージョンへの読み取りを拒否するだけでは不十分です。

動的シークレットの場合、コネクタはワークロードに対して一意の短命な認証情報を作成し、リースを記録します。更新時には、要求された期間よりも短い可能性のある実際の新しい有効期限が返されます。有効期限切れまたは手動失効が発生すると、ターゲットに認証情報の無効化を指示します。ターゲットに到達できない場合、リースは REVOCATION_PENDING 状態に移行し、リトライ、アラート、およびオペレーター用ランブックがアクティブなままになります。キューメッセージを送信したという理由だけで、サービスがそれを失効済みとマークしてはなりません。

ステップ7:キャパシティプランニングと高負荷テナントの分離

2,000万個の名前、保持バージョン数3、値のサイズ2 KiBの場合、生の暗号化値のデータ量は概算で以下のようになります。

text
20,000,000 × 3 × 2 KiB = 122,880,000,000 bytes ≈ 114 GiB

暗号化されたメタデータが1バージョンあたり平均1 KiB追加されると、さらに約57 GiBが加算されます。3つのリージョンレプリカを考慮すると、インデックス、監査ログ、リース、冪等性レコード、バックアップ、および将来の増加分を除いた初期ストレージの見積もりは約513 GiBになります。この計算はプロンプトに基づくサイジングのベースラインであり、製品のベンチマークではありません。

毎秒100,000件の読み取りと平均2 KiBの返却値の場合、TLSやレスポンスのオーバーヘッドを除いても、平文のアウトバウンド転送量だけで毎秒約195 MiBに達します。安定したテナント識別子によってテナントをシャーディングし、極端にトラフィックが多いテナントや規制対象のテナントを専用セルに隔離します。テナントごとに同時実行数とレートのバジェットを適用し、侵害された1つのワークロードがすべての復号ワーカーや監査キャパシティを消費できないようにします。

読み取りレイテンシは、アイデンティティ検証、ポリシー評価、メタデータ検索、キーのアンラップ/キャッシュ、復号、監査キューイング、およびネットワーク時間に分解して評価する必要があります。HSMのキャッシュミスと監査ログのバックプレッシャーには、独立したレイテンシバジェットを割り当てます。p99を守るために監査ログを暗黙的に破棄してはなりません。耐久性のあるローカル/アウトボックス境界を使用するか、必要な監査レコードを保持できない場合は機密リクエストを拒否します。

ステップ8:リージョンリカバリとルートリカバリの個別設計

暗号化された状態と監査アウトボックスのレコードを、測定されたレプリケーション遅延を伴って、ウォーム状態のディザスタリカバリ(DR)リージョンへ非同期にレプリケーションします。昇格時にはフェンシングエポックを使用して、旧ホームリージョンが2つ目のプライマリとして書き込みを再開できないようにします。提示されたRPO 1分の条件下では、リージョン全体が失われた後、確認応答済みの最新バージョンを再作成する必要がある場合があることを呼び出し元が理解しておく必要があります。これが受け入れられない場合は、書き込みを確認応答する前にクロスリージョンクォーラムを要求します。

DRリージョンには、トラストルートサービス、稼働するワークロードアイデンティティ、ポリシー状態、および監査エクスポートへの独立したアクセスも必要です。暗号文のみをコピーすることはリカバリ計画とは言えません。暗号化されたスナップショットとポイントインタイムリストア(PITR)をテストし、構成情報、ブート認証情報、および自動アンシール素材は、データスナップショットが暗号化されていても機密性が高いため、個別に保護します。

ルートリカバリには、クォーラム制御されたブレークグラス手順、個別の管理者、改ざん不能な証拠、および定期的な訓練を用います。テナントキーを紛失すると、そのテナントの値へのアクセスが失われる可能性があります。キーが漏洩すると、そのキーでラップされたすべての値が公開される恐れがあります。したがって、キーのバックアップ、ローテーション、配置、および破棄には、明示的なライフサイクルテストが必要です。

ステップ9:セキュリティ特性と障害時の動作の検証

正常系のレイテンシだけでなく、不変条件に基づいたテストを構築します。

  • テナント間をまたぐパス、エンコードされたパス、ワイルドカードポリシー、および期限切れのアイデンティティを用いたリクエストを生成し、すべてが拒否されることを証明します。
  • バージョン作成、昇格、ポリシー失効、および読み取りを競合させ、新たに拒否されたバージョンを配信する古いノードが存在しないことを確認します。
  • データベース、キュー、ログ、トレース、クラッシュダンプ、およびバックアップをスキャンし、カナリア用の平文やアンラップされたキー素材が存在しないか検証します。
  • KMSのレイテンシ、キーキャッシュの退避、ストレージクォーラムの喪失、監査バックプレッシャー、および完全なコントロールプレーン停止を注入(フォールトインジェクション)します。
  • ターゲット側のアクションの前後で各ローテーションステージを中断し、重複した認証情報の作成や誤った完了判定を起こすことなく、リコンシリエーションが収束することを証明します。
  • 有効期限切れの処理中に動的シークレットのターゲットを利用不可にし、ターゲット側での無効化が成功するまでリースが保留中(Pending)として表示され続けることを証明します。
  • 暗号化されたレプリケーションとスナップショットからDRリージョンを復元し、フェンシング、アイデンティティ、ポリシー、キー、監査、RTO、および実測RPOを検証します。
  • テナントの分布が極端に偏った状態で、毎秒100,000件の読み取りと2,000件の書き込みの負荷テストを実施し、p99、拒否判定の正確性、HSMトラフィック、キャッシュスコープ、および監査の完全性を測定します。

質の高い回答例

「まず、本サービスはストレージ、バックアップ、テナント間、ロギング、および限定的なホスト侵害から保護する設計とします。ただし、正当に認可されたワークロードが受け取った値を悪用することは防げません。すべてのリクエストは短命なワークロードアイデンティティから始まります。サービスはそのアイデンティティからテナントとワークロードを導出し、特定のリソースとアクションに対してバージョン管理されたポリシーを評価します。呼び出し元が指定したパスによって別のテナントが選択されることはありません。

イミュータブルなバージョン管理を採用します。作成リクエストは保留中(Pending)のバージョンを書き込み、昇格処理は期待される以前のバージョンからの条件付き更新によってシークレットの current ポインタを移動します。ホームリージョン内の3ゾーンクォーラムが、暗号文、メタデータ、冪等性、および監査アウトボックスをまとめてコミットします。current の読み取りには読み取りバリアを使用し、古いレプリカが失効済みのバージョンを提供しないようにします。

暗号化については、各バージョンにランダムなデータキー(DEK)を割り当て、テナント、シークレット、バージョンにバインドされた認証付き暗号(AEAD)を使用します。テナントキー(KEK)がそのデータキーをラップし、HSMまたはKMSで保護されたルートが認可されたデータプレーンノード上でのみテナントキーをアンラップします。通常のストレージとバックアップには、暗号文とラップされたキーのみが含まれます。読み取りノードは、強化されテナントごとにパーティション分割されたメモリ内にアンラップされたキーを短時間キャッシュできますが、平文が共有キャッシュ、ログ、トレース、スワップ、コアダンプに入ることは決してありません。

認証情報のローテーションは、作成、ターゲットへのインストール、検証、昇格、オーバーラップ、古いターゲット認証情報の失効、完了というステートマシンとして実行します。外部データベースはメタデータのトランザクションに参加できないため、コネクタのすべての呼び出しは冪等性を持ち、調整(リコンシリエーション)されます。緊急時の侵害対応ではオーバーラップを排除する場合があります。動的認証情報はリース付きでワークロードごとに発行されます。ターゲットが無効化を確認して初めて期限切れが完了し、それ以外の場合はシステムは保留状態を維持してアラートを発報します。

コントロールプレーンがポリシー、バージョン、承認、ワークフローを管理し、水平スケールされたデータプレーンセルが読み取りを処理します。暗号文は広くキャッシュ可能で、ポリシーには有効期限が制限されたバージョン管理キャッシュを用い、アンラップされたキーのキャッシュは局所的かつ短期間にとどめます。リージョン喪失に対しては、暗号化された状態をウォームリージョンにRPO 1分でレプリケーションし、フェンシングエポックによってスプリットブレインを防ぎます。RPO 0の要件に変更された場合は、リージョン間で同期コミットを行い、レイテンシの増加や書き込み可用性の低下を受け入れます。

最後に、テナント間攻撃やエンコードパス攻撃、並行した昇格と失効、すべての可観測性およびバックアップ領域へのカナリア漏洩、KMSおよび監査の停止、各ローテーションステップでのクラッシュ、リースの失効スタック、および完全なDRリストアをテストします。これらの障害下でもセキュリティ決定が正しく維持され、毎秒100,000件の読み取りピーク時にも測定されたレイテンシバジェットを満たして初めて、本設計は合格となります。」

よくある落とし穴

  • データベースの隣にある単一のデータベース暗号化キーを使用する → スナップショットが侵害された場合に暗号文とその復号パスの両方が漏洩します → HSM/KMSルート、テナントラッピングキー、バージョンごとのデータキー、および通常ストレージを分離してください。
  • リクエスト内のテナントやパスを信用する → オブジェクト名の変更がテナント間アクセスにつながります → 検証済みアイデンティティからテナントを導出し、一度だけ正規化し、特定のアクションとリソースを認可してください。
  • デバッグのためにリクエストまたはレスポンスのボディをログ出力する → 可観測性スタックが平文シークレットの保管場所になってしまいます → 値を含まない識別子、判定結果、バージョン、およびカナリア漏洩テストを使用してください。
  • 読み取りごとにHSMを呼び出す → トラストルートのレイテンシとクォータがプラットフォーム全体のボトルネックになります → エンベロープ暗号化を使用し、影響範囲を明確にした保護メモリ内のキーキャッシュを活用してください。
  • 復号された値をRedisに配置する → レイテンシの最適化が、広範な平文漏洩の境界を生み出します → 暗号文を広くキャッシュし、平文は認可されたコンシューマーの制限されたメモリ内にのみ保持してください。
  • ローテーションを単一の更新操作として扱う → ターゲットへのインストールが成功してもメタデータの更新が失敗する(またはその逆の)事態が発生します → 耐久性のある段階的ワークフロー、冪等なコネクタ操作、オーバーラップルール、およびリコンシリエーションを使用してください。
  • メッセージを発行しただけでリースを失効済みとマークする → ダウンストリームの認証情報がまだ有効なまま残る可能性があります → ターゲット側での無効化が確認されるまで保留状態を維持し、失効がスタックした場合はアラートを発報してください。
  • コピーされた静的値の即時失効を保証してしまう → 将来の読み取りは拒否できますが、既存のコピーは使用可能なまま残ります → 認証情報を受け入れるシステム側でローテーションまたは無効化を行い、短命な動的認証情報を優先してください。
  • 無期限に古いポリシーを安全側に倒さず許可(Fail-open)してしまう → 削除されたプリンシパルが機密値を読み取り続けられるようになります → ポリシーキャッシュをバージョン管理して無効化し、最大有効期間を適用して、制限時間を超えた場合は安全側に倒して拒否(Fail-closed)してください。
  • 暗号文を第2のリージョンにコピーしただけでDRと呼ぶ → アイデンティティ、ルートキー、ポリシー、フェンシング、および監査が利用できないままになる可能性があります → 完全なリカバリ依存関係グラフをテストし、実際のRTOとRPOを測定してください。

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

フォローアップ1:シークレットマネージャーとKMSの違いは何か?

シークレットマネージャーは、不透明な認証情報の保存とバージョン管理、取得の認可、ローテーションの調整、リースの発行、およびアクセスの監査を行います。KMSは暗号鍵を管理し、ラッピングや署名などの暗号操作を実行します。本設計では、KMSまたはHSMをトラストルートとして使用します。そのルートを通常の取得可能なシークレットとして公開することはありません。

フォローアップ2:アプリケーション内でシークレットをキャッシュできるか?

はい、明示的な規約がある場合に限り可能です。エージェントは許可された値を与えられたTTLまでプロセスプロセスメモリ内に保持し、有効期限が切れる前にリフレッシュできます。これにより可用性の依存度と読み取り量が削減されますが、ポリシーの失効単独では既存のコピーを即座に破棄できない期間が長くなります。リスクの高い認証情報や動的認証情報には、無制限のキャッシュではなく、より短いリースとターゲット側での失効を使用する必要があります。

フォローアップ3:KMSまたはHSMが利用できなくなった場合はどうなるか?

ノードは、承認されたキャッシュ有効期間内にあるアンラップ済みのキーのみを使用して処理を継続できます。新規テナント、キャッシュミス、およびルートキー操作は安全側に倒して失敗(Fail-closed)します。キャッシュのカバー率、KMSのエラー率、および残りの安全ウィンドウを追跡し、すべてのノードの有効期限が同時に切れる前に負荷を遮断(ロードシェディング)します。インシデント中にキーの有効期限を延長することはセキュリティ上の決定事項であり、自動リトライループではなく事前定義されたポリシーが必要です。

フォローアップ4:すべてのシークレットを復号せずにテナントキーをローテーションするにはどうすればよいか?

古いテナントキーでバージョンごとの各データキー(DEK)をアンラップし、新しいテナントキーでラップします。平文のデータキーが境界の外に出ないよう、信頼できるキーサービス内で行うのが望ましいです。テナントキーのバージョンとラップされたデータキーの両方を保存し、移行のチェックポイントを記録し、すべての参照およびバックアップポリシーで廃止が許可されるまで古いテナントキーを保持します。暗号化されたシークレット値自体は変更されません。

フォローアップ5:特権オペレーターがすべてのテナントのシークレットを読み取るのを防ぐにはどうすればよいか?

ポリシー管理、キー管理、インフラ運用、および監査レビューの職務を分離します。日常的な人間による平文取得を無効化し、例外時にはクォーラム承認された短期間の許可を要求し、規制対象のテナントを個別のセルとキースコープに配置し、独立して管理された監査システムに証跡を送信します。日常業務で使用する単一のロールが、外部証跡なしに自身のアクセス権を昇格させ、かつ値を復号できるようにしてはなりません。

フォローアップ6:ホームリージョンがダウンしている間も読み取りを利用可能にすべきか?

DRリージョンがフェンシングされた確定的なエポック、十分に最新のポリシーと暗号化データ、トラストルートへのアクセス権、および明確に合意されたRPOを保持している場合に限られます。任意の古いレプリカから提供すると、失効したアクセス権が復活する恐れがあります。提示されたRPO 1分の条件下では、テスト済みのフェイルオーバープロセスを通じてウォームリージョンを昇格させます。RPO 0を実現するには、インシデント発生前からの同期的なクロスリージョンコミットが必要です。

フォローアップ7:シークレットを環境変数としてのみ保存してはいけない理由は何か?

環境変数は配信メカニズムであり、ライフサイクル管理ではありません。プロセスの検査、クラッシュダンプ、診断情報、子プロセス、またはデプロイ設定を通じて公開されるリスクがあります。また、中央でのバージョン管理、ターゲット側でのローテーション、リース、および読み取りごとの属性追跡(アトリビューション)が欠如しています。デプロイエージェントが環境変数を注入する場合は、そのスコープと有効期間を最小限に保ち、シークレットマネージャーをポリシーとライフサイクルの真実のソース(Source of Truth)として維持してください。

公開情報ソース

関連する質問

関連面接ツール

システム設計の回答には「回答する」を使用

まず要件を明確にし、スケール、アーキテクチャ、コンポーネント選定、トレードオフの順に進めます。

ツールを見る