コンテンツにスキップ

PII分離アーキテクチャ

Authrim は、機微な属性値を、認証・認可に使うアカウントの状態から分けて保存します。一つのアカウントの Core データと PII データを別の D1 データベースに配置し、読み書きの前にそのアカウントの保存先を解決します。

このページでは、現在の Control Plane によるルーティングと canonical identity モデルを説明します。PII データベース以外の情報がすべて匿名になるわけではありません。また、保存先の分離そのものが暗号化を行うわけでもありません。

以下は論理的な役割です。「決まった三つのバインディングに保存する」という構成ではなく、一つのデプロイに複数の Core・PII・Lookup データベースを持てます。

役割保存する情報と責務
Core主体・アカウントとその関連、ライフサイクルや検証状態、プロフィール属性のメタデータ、機微な値への参照。テナントのメタデータも、解決された Core の保存先を使います。
PIIidentity_sensitive_values の機微な属性値、外部 ID との連携、主体の識別子、削除記録。互換用の users_pii などもスキーマに残っています。
Lookupアカウントの Core・PII 保存先を探すためのブラインドインデックスとルーティング情報。属性値の正本を保存する場所ではありません。
Admin / Control管理情報、データベースの台帳とレジストリ、配置・ルーティング情報、フィールドカタログ、運用状態。アカウントの PII 保存先とは分かれています。

KV にも、署名付きのランタイムレジストリ情報やキャッシュを保存します。プロトコルの状態やその他のキャッシュには、それぞれの保存経路があります。D1 の役割を分けても、ユーザーに関係する情報が D1 だけに存在するわけではありません。

アカウントの保存先を決める仕組み

Section titled “アカウントの保存先を決める仕組み”

D1 の配置とルーティングは Control Plane が管理します。現在のランタイムプロファイルは監査の配送先とデータ所在地のポリシーを設定するもので、shared-d1・tenant-d1・external-durable といった保存構成を選ぶものではありません。

runtime-data-context.ts が使うアカウントの経路では、次の処理を行います。

  1. ランタイムレジストリから取得した Lookup のバケット割り当てと HMAC 鍵の状態を検証します。
  2. メールアドレス、外部の発行者と subject の組、またはアカウント ID からブラインドインデックスを生成して検索します。指定テナント内で、一つのアカウントに確定する必要があります。
  3. 署名付きのレジストリ情報を使い、tenant_core/users と tenant_pii の保存先を解決します。バインディング、シャード、データ所在地の区分、ルートの世代を確認します。
  4. 実際の接続先でも、シャードのメタデータやアカウントの状態を再検証します。Core だけで完結する処理では、PII の値を読み出さずに処理できます。
flowchart TB
    Request["テナントとアカウント識別子"] --> Lookup["Lookup:インデックスと保存先"]
    Lookup --> Verify["レジストリと接続先を検証"]
    Verify --> Core["Core:状態と値への参照"]
    Verify --> PII["PII:機微な属性値"]

レジストリのリゾルバーは、Core と PII が物理的に分かれていることも検証します。バインディングの欠落、世代の不一致、隔離されたルート、署名の不正はエラーとなり、任意の共有データベースへ切り替えて処理を続けることはありません。

このルーティング経路が現在受け付けるプロバイダーは D1 です。Authrim の別の箇所に PostgreSQL や MySQL のアダプターがあっても、この経路の保存先としてそのまま差し替えられるわけではありません。

ランタイムのユーザー操作は、CanonicalRuntimeUserStore、その投影用リポジトリ、CanonicalRuntimeUserWriter を使います。テナントを限定した Core アダプターと、解決済みの PII アダプターを組み合わせます。

データ現在の表現
主体・アカウントの状態Core の identity_subjects、identity_accounts、subject_account_links。ライフサイクル、登録状態、ディレクトリへの公開状態、ルートの世代などを持ちます。
プロフィール属性Core の profiles と profile_attribute_values。値を直接持つ属性と、機微な値への参照を持つ属性があります。
連絡先Core の contact_points に種別、検証状態、値への参照などを保存します。このメタデータと、参照先のメールアドレス・電話番号の値は別です。
機微な属性値PII の identity_sensitive_values に、tenant_id、所有者、値のキー、value_json、分類、ライフサイクル状態を保存します。
外部・主体の識別子linked_identities や subject_identifiers は PII 側にあります。識別子を一律に「Core だけにある情報」とは扱えません。

書き込み処理では、piiFields と sensitiveValues で機微なプロフィール値を指定します。一方、inlineProfileFields は Core に属性値を直接保存します。そのため、実際の保存先はフィールドの分類と呼び出し経路にも依存します。個人を識別し得る情報がすべて自動的に PII へ移る仕組みではありません。

Core にも、保存する場合のパスワードハッシュなど、保護が必要なアカウントのメタデータがあります。Core に対するアクセス制御やログの扱いも必要です。Core は保存上の役割であり、「公開してよい」「個人情報ではない」という分類ではありません。

Lookup では、正規化と鍵の世代を管理した HMAC のブラインドインデックスを使います。元の識別子を検索キーとして保存せずに、アカウントの保存先を探すための仕組みです。ただし、スキーマ内のハッシュらしい名前のフィールドがすべて同じ仕組みというわけではありません。連絡先の参照や value_hash は別の用途を持ち、書き込み処理が value_hash を空にする場合もあります。

プロフィールの値が必要な処理では、PII を読みます。ユーザーストアにも、メールアドレスやユーザー名の検索など、機微な値を検索するメソッドがあります。すべての認証・検索が PII に触れずに完結するわけではありません。

canonical の書き込み処理は、機微な値を JSON 化して value_json に保存します。この経路で値ごとのアプリケーション層の暗号化は行っていません。保存先の分離、データベース・プラットフォーム側の暗号化、アプリケーション層の暗号化は、それぞれ別の対策です。PII ログ用の暗号化機能も、プロフィールの値がすべてその機能で暗号化されるという意味ではありません。

保存先を解決して実行する Admin のユーザー削除処理は、状態のフラグを変えるだけではありません。

  1. アカウントを解決し、有効な法的保全(legal hold)の有無を確認します。保全中の場合は削除を拒否します。
  2. 認証状態を deleting に移し、セッションの失効を要求します。
  3. アカウントディレクトリからの除去を準備し、削除記録がなければ作成します。
  4. ランタイムユーザーの機微な value_json を消去し、対象の外部・主体識別子を削除します。識別子変更の処理に残る値も、削除ルーチンの対象範囲で消去します。
  5. アカウントと主体を削除済みにし、ディレクトリからの除去を配送・再試行できる状態にします。ユーザーキャッシュを無効化し、削除イベントを記録します。

CanonicalRuntimeUserWriter.deleteRuntimeUser() 単体はライフサイクルを変更する処理であり、上記の消去処理全体の代わりにはなりません。アカウント削除によって Core の行や保持対象の削除・監査記録がすべて物理削除されるわけでもありません。エクスポート、アーカイブ、他のコピー、連携アプリ側のデータには、それぞれの保持・削除処理が必要です。

users_pii_tombstone には、元のユーザー ID、テナント、削除日時、実行者・理由、保持期限、任意のメタデータとメールのブラインドインデックスを保存します。リポジトリの既定の保持期間は 90 日です。保存先を解決する Admin の削除処理も 90 日を指定しますが、メールのブラインドインデックスは空のままです。

そのため、この経路の削除記録を「同じメールアドレスでの再登録を自動的に防ぐ仕組み」として説明することはできません。また、元のメールアドレスがなくても、削除記録が必ず匿名情報になるわけではありません。

保持期限を過ぎた記録は、メールの削除記録を照合する際の有効な対象から外れます。物理削除は別のクリーンアップ処理で行い、Admin API にその操作があります。保持期限のカラムは D1 の行を自動消去する TTL ではありません。

  • Core・PII・Lookup・管理情報の実際の内容に応じてアクセスを制限し、接続先だけでなくテナントの所有関係も検証します。
  • カスタム属性、ラベル、メタデータを Core に直接保存する前に、フィールドの分類を確認します。
  • レジストリの署名、ルートの世代、Lookup の鍵ローテーションを運用対象に含めます。エラー復旧のためにルート検証を迂回しないでください。
  • 監査の記録内容、キャッシュ、エクスポート、保持設定は個別に確認します。削除イベントがあることは、すべての PII 読み出しの監査や、すべてのコピーの消去を保証しません。
  • Core・PII・Lookup をまたぐ書き込みは、失敗・再試行を調整する複数の処理です。データベースをまたぐ一つの SQL トランザクションとしては扱えません。

このページの説明に対応する主なソースファイルです。

  • packages/ar-lib-core/src/services/runtime-data-context.ts
  • packages/ar-lib-core/src/services/tenant-database-resolver.ts
  • packages/ar-lib-core/src/types/runtime-profile.ts
  • packages/ar-lib-core/src/services/lookup-directory/blind-index.ts
  • packages/ar-lib-core/src/repositories/identity/canonical-runtime-user-writer.ts
  • packages/ar-management/src/admin-users.ts
  • packages/ar-management/src/account-directory-removal-producer.ts
  • packages/ar-lib-core/src/repositories/pii/tombstone.ts