N
NextTech Insights
マルチテナントRAGの認可チェックリスト|テナント間の情報漏えいを防ぐ(2026)
airagsecurityauthorization

マルチテナントRAGの認可チェックリスト|テナント間の情報漏えいを防ぐ(2026)

17 min read

マルチテナントRAGの認可を実装・監査するための実務チェックリスト。サーバー側でテナント境界を確定し、検索時に強制し、チャンクACL・キャッシュ・禁止取得テストまで整えます。

目次

マルチテナントRAGで、他社データの誤取得をどう防ぐ?

1分要約

  • テナント・ユーザー・所属グループは、検証済みセッションからサーバー側で確定する。ブラウザから届く tenantId、namespace、ACLは認可に使わない。
  • 認可はベクトル検索や全文検索のクエリ内部で強制する。全テナントを検索した後に結果を除外する方式は安全ではない。
  • ACLをチャンク単位で保持し、semantic cacheやログにも境界を引き継ぐ。取得禁止文書が一件も混ざらないことをリリース条件にする。

どんな人向けか

  • 複数顧客の文書を1つのRAG基盤で扱うSaaSチーム
  • 既存の認可モデルへベクトル検索、ハイブリッド検索、reranker、semantic cacheを追加するエンジニア
  • テナント間漏えいを監査可能なテストへ落としたいセキュリティ・基盤担当者

結論

retrievalを「ログイン後に使える検索機能」ではなく、認可境界として扱う。

安全な処理順は次の通り。

  1. 呼び出し元を認証する
  2. テナント、ユーザー、グループ、ロールをサーバー側で確定する
  3. 可能ならindex、collection、namespaceなどの隔離境界を選ぶ
  4. retrievalリクエスト内部で文書・チャンクACLを適用する
  5. 認可済み候補だけをrerankする
  6. 生成前に最終contextを再検証し、違反時はfail closedにする

検索後のフィルタは主制御にならない。その時点で、他テナントのvectorが件数、処理時間、score、候補選択、ログ、rerankerへ影響している可能性がある。OWASP GenAI LLM Top 10 2026もembedding層をアプリケーションのtrust boundaryに含め、index queryの内部でtenant scopeを強制するよう勧めている。

機密性が高いテナントやtrust zoneは、物理的に分けたindexや強いnamespace隔離を優先する。共有indexとmetadata filterを使う場合も、すべてのread/write経路でサーバー由来のscopeを必須にし、迂回経路を負のテストで塞ぐ。

解説

認証できても、文書の認可は終わっていない

認証は「誰がアプリを呼んだか」を確かめる。retrievalが返した各チャンクを、その人が読めることまでは保証しない。

RAGのリクエストは、複数の保存先と変換処理を通る。

  • 元文書と元システムのACL
  • 抽出テキストとチャンク
  • embeddingとvector metadata
  • keyword・vector検索の候補
  • rerankerへの入力と出力
  • 最終prompt contextと引用
  • semantic cache、trace、サポート用ログ

どこか1段でもtenantやACLのscopeが消えると、通常の製品画面では見えない情報が回答へ混ざる。

top-kの後ではなく、similarity searchの前に絞る

次の順序は危険。

全テナントを検索 -> 上位20件を取得 -> 権限のない結果を削除 -> 残りを生成へ渡す

他テナントのチャンクが上位を占有すると、正しいテナントの回答が空になる。文書本文を返さなくても、結果件数、score分布、latency、trace、後段処理から「何かが存在する」と推測される余地も残る。

必要な順序はこちら。

検証済み認可scope -> scope付き検索 -> 認可済み候補 -> rerank -> 最終context

Azure AI Searchはtenant filterやdocument-level security filterをquery-time controlとして説明している。Pineconeはserverlessのマルチテナント構成で1 tenant = 1 namespaceを推奨し、data-plane操作は必ず1つのnamespaceを対象にする。他製品ではtenant partition、collection、shard、prefilterなど名称が違うが、必要な性質は同じ。権限のないvectorを候補集合へ入れないことだ。

tenant隔離と文書ACLは別の問題

tenant境界は「このデータはどの顧客に属するか」を決める。ACLは「その顧客の中で、誰がこのチャンクを読めるか」を決める。

1 tenant = 1 namespaceでも、社内に人事・法務・経理・案件別の限定文書があるなら不十分。元データに細かい権限がある場合は、次の2層を保つ。

  • tenantまたはtrust zoneの隔離
  • 文書・チャンク単位のuser、group、role、classification

1つの文書に公開部分と機密部分が混在するなら、チャンク単位で認可する。ACL metadataが欠けた、または同期期限を過ぎた場合は公開扱いにせず、取得を拒否する。

実務ガイド

Step 1: 認可contractを先に決める

vector databaseを選ぶ前に、アクセスを決める属性を明文化する。

  • 組織境界となる tenant_id
  • 認証済み user_id
  • 有効なgroup IDとrole ID
  • project、region、classificationなどのscope
  • ACL versionまたは最終同期時刻

これらは検証済みsessionか信頼できるidentity providerから作る。request body、query parameter、モデル出力、検索済みテキストを認可根拠にしない。

Step 2: 機密度に合わせて隔離境界を選ぶ

次の順で判断しやすい。

  • 専用serviceまたはindex: 規制対象、契約上の分離が必要、特に機密性が高いtenant
  • namespace・tenant partition・collection: 共有基盤上で行う一般的なSaaSのtenant隔離
  • 共有index + サーバー強制metadata filter: 機密度が低い共有データ、または複数tenantの横断検索が本当に必要な場合

embedding dimensionが同じという理由だけで、外部Web、社内文書、顧客限定データを同じtrust zoneへ入れない。

Step 3: 取り込み時にACLを保存する

すべてのチャンクへ、元データの認可判断を再現できるmetadataを持たせる。

{
  "chunk_id": "refund-policy#annual-plans-02",
  "tenant_id": "tenant_7",
  "source_id": "refund-policy",
  "allowed_group_ids": ["billing", "support-leads"],
  "classification": "internal",
  "acl_version": "2026-09-05T02:10:00Z"
}

取り込み処理は必須scopeの存在と形式を検証する。認可metadataが欠けたレコードは隔離し、indexへ入れない。元システムの権限が変わったら、決めたSLO内で該当チャンクを更新または再indexする。

Step 4: 信頼できるアプリコードでscopeを組み立てる

retrieval関数へ渡す値を絞る。次のTypeScriptは製品非依存の疑似コードなので、filter構文は利用中の検索製品へ合わせる。

type AuthzContext = {
  tenantId: string;
  userId: string;
  groupIds: string[];
};

async function retrieve(query: string, sessionToken: string) {
  const authz: AuthzContext = await authzFromVerifiedSession(sessionToken);

  const results = await vectorStore.query({
    namespace: `tenant:${authz.tenantId}`,
    query,
    topK: 12,
    filter: {
      tenant_id: authz.tenantId,
      allowed_group_ids: { anyOf: authz.groupIds },
    },
  });

  if (results.some((r) => !isAuthorized(r.metadata, authz))) {
    throw new Error('retrieval_scope_violation');
  }

  return results;
}

最後の検証はdefense in depthと異常検知のために置く。全体検索してから弾いてよい、という意味ではない。

Step 5: rerankと生成までscopeを切らさない

rerankerへ渡せるのは認可済み候補だけ。promptを組み立てる直前にもchunk IDを検証し、ユーザーが開けない引用リンクは表示しない。

「このtenantの文書だけを使って」とLLMへ頼んでも認可にはならない。system promptはモデルへの行動指示であり、決定的なaccess controlではない。

Step 6: cache・trace・派生データも隔離する

retrievalが正しくても、派生システムがscopeを落とすと漏えいする。

  • semantic cache keyへtenantと認可scope versionを含める
  • source setが明示的にpublicでない限り、生成済み回答をtenant間で共有しない
  • trace、eval、サポートツールは同等以上の境界で保護する
  • embeddingとvector backupを元文書と同じ機密区分で扱う
  • 元文書を削除したらchunk、vector、cacheも削除する

信頼できないclientへraw similarity scoreを返さない。特定文書の存在を探るoracleになり得る。

Step 7: 「取得してはいけない」をテストする

少なくとも2 tenant、各tenant内に複数のpermission groupを用意する。質問ごとに、期待するchunk IDだけでなく禁止chunk IDも記録する。

release gateへ次を入れる。

  • candidate retrievalに禁止IDが0件
  • rerank後とcontext assembly後も禁止IDが0件
  • 互換性のないscope間でcache hitが発生しない
  • tenantまたはACL metadata欠落時にdeny by defaultとなる
  • 権限取り消しが定義済みの同期期限内に反映される
  • 直接的なID、言い換え、攻撃的なprobe queryで同じ境界を守る

productionと同じAPI経路で試す。filter builderだけのunit testでは、scopeなしfallback queryを見つけられない。

Step 8: 監視と事故対応を用意する

tenant scope、principalまたはgroup ID、index version、返したchunk ID、policy判定を残す。質問文に機密情報が入り得るなら、retention policyに従ってredactまたはhash化する。

次をalert対象にする。

  • tenantまたはACLが一致しないchunkを1件でも返した
  • scopeなしの検索request
  • 拒否やtenant横断probeの反復
  • embedding・similarity search APIへの異常なアクセス
  • 合意した期限を超えるACL同期遅延

vector storeやembedding backupが漏えいしたとき、「vectorだけだから影響は小さい」と決めつけない。機密元データと同じincident processへ上げる。

ありがちな落とし穴

  • ブラウザから届く tenantId、namespace、group ID、filter JSONを信頼する
  • search APIを認証しただけで、各chunkを認可しない
  • 全体top-kを取得してからtenant filterをかける
  • vector searchにはscopeを付けたが、keyword searchやfallback searchで忘れる
  • 認可済み・未認可の候補を一緒にrerankする
  • tenant namespaceを分けただけで、tenant内の文書ACLを無視する
  • ACL metadataが欠けたchunkをpublic扱いする
  • 共有semantic cacheから別tenantへ回答を再利用する
  • 削除・権限取り消し済みのデータをvector、backup、cacheへ残す
  • raw similarity score、件数、詳細traceをclientへ返す
  • 正常系だけをテストし、禁止IDが混ざらないことを検証しない
  • system promptへaccess controlを任せる

チェックリスト

  • [ ] tenant scopeは検証済みのサーバー側sessionから作る
  • [ ] client由来のtenant、namespace、ACL値は認可に使わない
  • [ ] すべての取り込み経路で必須tenant・ACL metadataを書き込む
  • [ ] 認可metadataの欠落・不正はfail closedにする
  • [ ] 高機密tenantまたはtrust zoneは強い隔離境界を使う
  • [ ] vector・keyword・hybrid・fallbackの全検索でquery内部にtenant scopeを強制する
  • [ ] tenant隔離に加えて、文書・chunk ACLも強制する
  • [ ] rerankerへは認可済み候補だけを渡す
  • [ ] 生成前に最終contextと引用を検証する
  • [ ] semantic cache keyへtenantと認可scopeを含める
  • [ ] trace、eval dataset、export、サポートツールも同じ境界を守る
  • [ ] embeddingとbackupを元文書と同じ機密区分で扱う
  • [ ] 元文書削除時にchunk、vector、cache、派生物も消す
  • [ ] 権限変更を反映する同期期限を定義する
  • [ ] retrieval logへ監査に必要なIDとpolicy判定を残す
  • [ ] raw similarity scoreを信頼できないclientへ返さない
  • [ ] 2つ以上のtenantと複数permission groupでテストする
  • [ ] 禁止文書の取得件数0をrelease gateにする
  • [ ] 直接質問・言い換え・攻撃的probe queryをテストする
  • [ ] tenant横断のvector露出をdata-security incidentとして扱う

FAQ

1. 1 tenant = 1 namespaceなら十分?

vector基盤が確実なnamespace隔離を提供するなら、強いtenant境界になる。ただし、tenant内の権限までは解決しない。人事・法務・経理・案件文書で閲覧者が異なるなら、文書またはchunk ACLも必要。

2. 1つの共有indexでmetadata filterを使ってもよい?

リスクと製品仕様が合えば可能。ただしfilterは必須で、サーバーが検証したidentityから作り、retrieval内部で適用する。fail closedの初期値と負のテストも必要。高機密用途は、より強い隔離境界を優先する。

3. 最後に未認可chunkを除外すれば、本文は漏れないのでは?

本文を表示しなくても、未認可vectorがtop-k、結果件数、score、処理時間、reranker、trace、cacheへ影響する。未認可chunkが候補枠を使い、正しい回答を落とす問題もある。

4. embeddingも機密データとして扱うべき?

扱うべき。OWASPの現行ガイドはembedding層をtrust boundaryに含め、embeddingやbackupから元データの情報が露出し得ると警告している。元文書と同等のaccess、暗号化、retention、incident controlを適用する。

参考資料

内部リンク

免責事項

一般的な開発・セキュリティ情報です。利用中のvector製品、identity基盤、データ分類、契約、法規制に合わせて隔離設計を検証してください。

人気記事

  1. 1Permit2とは何か?Approveが変わった理由と安全な使い方(チェックリスト)
  2. 2署名(Sign)画面の読み方|Approve/Permit/NFT全権限を30秒で判別するチェックリスト
  3. 3仕様→実装に落とすプロンプトテンプレ(AI開発)|AIに“迷わせない”仕様の書き方
  4. 4EVMのトークン承認(Approve)を見直す方法|Revokeの手順と判断基準(チェックリスト)
  5. 5曖昧な依頼を要件定義に変換する質問リスト(AI開発)|AIに実装させる前に聞くべきこと

関連記事