複数の企業が1つのSaaSを利用する「マルチテナント型」のシステムでは、権限管理とデータ分離が非常に重要です。
例えば、顧客管理SaaSをA社とB社が利用しているとします。
当然ながら、
- A社のユーザーはA社の顧客だけ閲覧できる
- B社のユーザーはB社の顧客だけ閲覧できる
- A社の管理者がB社のユーザーを変更できない
という状態にしなければなりません。
さらにA社の中でも、
- 管理者
- 一般社員
- 閲覧専用ユーザー
によって操作可能な範囲を分ける必要があるかもしれません。
つまり、マルチテナントSaaSの権限管理では、
「どの企業に所属しているか」と「その企業内で何をしてよいか」を分けて設計すること
が基本になります。
例えば、
ユーザー
↓
所属企業(テナント)を確認
↓
対象データが同じテナントか確認
↓
そのユーザーに操作権限があるか確認
↓
処理を実行
という順番です。
この記事では、マルチテナントSaaSを想定して、企業ごとのデータ分離、tenant_idを使ったDB設計、RBACによる権限管理、管理者機能、監査ログなどを実務目線で解説します。
マルチテナントとは
マルチテナントとは、1つのシステムを複数の企業や組織で共有して利用する構成です。
SaaSではよく利用される方式です。
例えば、100社が利用する顧客管理SaaSがあったとしても、企業ごとに別々のアプリケーションを用意するとは限りません。
1つのWebアプリケーションを複数社で利用しながら、データだけを論理的または物理的に分離する構成があります。
ここでいう「テナント」は、SaaSを利用する企業や組織の単位と考えると分かりやすいでしょう。
例えば、
- A株式会社:tenant A
- B株式会社:tenant B
- C株式会社:tenant C
という形です。
マルチテナントの権限管理で最も重要なのは企業間のデータ分離
マルチテナントSaaSで特に避けなければならないのが、別企業のデータを閲覧・更新できてしまうことです。
例えばA社のユーザーが、
/customers/100
という顧客詳細画面を閲覧しているとします。
URLを、
/customers/101
へ変更したところ、B社の顧客情報が表示されてしまったとします。
これは重大な問題です。
画面上でB社の顧客を表示していなくても、サーバー側でテナント確認を行っていなければ、IDを書き換えることで別企業の情報へアクセスできる可能性があります。
そのため、
「ログインしているか」だけではなく、「そのデータがログインユーザーの所属テナントのものか」まで確認する
必要があります。
認証・テナント判定・権限判定は分けて考える
マルチテナントの設計では、3つの判定を分けると整理しやすくなります。
1. 認証
認証では、
「あなたは誰か」
を確認します。
例えば、
- メールアドレス
- パスワード
- SSO
- 多要素認証
などを使ってユーザーを識別します。
2. テナント判定
次に、
「どの企業に所属しているか」
を確認します。
例えば、
ユーザーID:1001
テナントID:company-a
という関係です。
3. 認可
最後に、
「その企業の中で何をしてよいか」
を判定します。
例えば、
管理者:
ユーザー追加・削除が可能
一般ユーザー:
顧客情報の登録・編集が可能
閲覧ユーザー:
閲覧のみ可能
という権限です。
この3つを一緒に考えてしまうと、実装が複雑になりやすくなります。
マルチテナントSaaSの基本的なデータ構造
代表的な構成として、tenant_idを各データへ持たせる方法があります。
例えば、企業を管理するテーブルがあるとします。
| tenant_id | company_name |
|---|---|
| 1 | A株式会社 |
| 2 | B株式会社 |
ユーザーには所属するtenant_idを持たせます。
| user_id | tenant_id | name |
|---|---|---|
| 101 | 1 | 山田 |
| 102 | 1 | 佐藤 |
| 201 | 2 | 鈴木 |
顧客情報にもtenant_idを持たせます。
| customer_id | tenant_id | customer_name |
|---|---|---|
| 1001 | 1 | A社顧客X |
| 1002 | 1 | A社顧客Y |
| 2001 | 2 | B社顧客Z |
この場合、A株式会社のユーザーが顧客一覧を取得するときは、
「tenant_idが1の顧客だけ取得する」
という条件を必ず加えます。
tenant_idを付ければ安全とは限らない
ここがマルチテナント設計で重要なポイントです。
データベースにtenant_idが存在するだけでは、企業間のデータ分離は実現できません。
例えば顧客詳細を取得するときに、
「customer_idが1001」
だけを条件にするとします。
この場合、tenant_idを確認していないため、別企業の顧客IDを指定された場合に取得できる可能性があります。
そのため考え方としては、
「customer_idが1001」
ではなく、
「customer_idが1001、かつtenant_idがログインユーザーのtenant_id」
という条件で検索します。
更新や削除でも同様です。
- SELECT
- UPDATE
- DELETE
すべてでテナント境界を意識する必要があります。
tenant_idをリクエストからそのまま信用しない
マルチテナントSaaSで避けたい設計の一つが、画面から送られてきたtenant_idをそのまま使用することです。
例えばブラウザから、
tenant_id = 1
が送られてきたとします。
攻撃者がこれを、
tenant_id = 2
へ変更できるのであれば、別企業として操作できる可能性があります。
そのため通常は、
ログイン
↓
サーバー側でユーザーを特定
↓
ユーザーに紐づくtenant_idを取得
↓
そのtenant_idを使って処理
という形にします。
「自分がどの企業なのか」をクライアント側から自由に指定できる設計にしないことが基本です。
1人のユーザーが複数テナントに所属する場合
SaaSによっては、1つのアカウントで複数の組織に所属できる場合があります。
例えばコンサルタントが、
- 自社
- 顧客A
- 顧客B
という複数ワークスペースを切り替えて利用するようなサービスです。
この場合、
ユーザー = テナント
という単純な関係ではありません。
ユーザーとテナントの中間に「メンバーシップ」を持たせる方法があります。
例えば、
| user_id | tenant_id | role |
|---|---|---|
| 100 | 1 | admin |
| 100 | 2 | viewer |
| 200 | 2 | member |
という形です。
同じユーザーでも、
A社では管理者
B社では閲覧のみ
という権限を持たせられます。
RBACを使った企業内の権限管理
テナントごとの分離ができたら、次は企業内部の権限を考えます。
代表的なのがRBACです。
RBACはRole-Based Access Controlの略で、「役割」に応じて権限を付与する方式です。
例えば、
管理者
- ユーザー管理
- 顧客登録
- 顧客編集
- 顧客削除
- CSV出力
- 設定変更
一般ユーザー
- 顧客閲覧
- 顧客登録
- 顧客編集
閲覧ユーザー
- 顧客閲覧
という役割を用意します。
ユーザーごとに細かな権限を一つずつ設定するより、管理しやすくなります。
「テナント管理者」と「SaaS運営管理者」は別物
マルチテナントSaaSでは、管理者という言葉に注意が必要です。
少なくとも、
- テナント側の管理者
- SaaS提供者側の管理者
を分けて考える必要があります。
テナント管理者
A社の管理者であれば、
- A社ユーザー追加
- A社ユーザー権限変更
- A社データ閲覧
- A社設定変更
などを行います。
しかし、B社の情報にはアクセスできません。
SaaS運営管理者
SaaS提供会社が、
- 契約企業管理
- 障害対応
- 問い合わせ対応
- 利用状況確認
などを行うための管理者です。
こちらは複数テナントへアクセスする必要が生じる場合があります。
この2種類を同じ「admin」というロールだけで管理すると、権限が曖昧になりやすいため注意が必要です。
SaaS運営者は顧客データを自由に見られる設計でよいのか
SaaS運営者だからといって、すべての顧客データを常時閲覧できる必要があるとは限りません。
例えば問い合わせ対応で、
「A社のこのデータがおかしい」
という調査が必要になることがあります。
その場合でも、
- サポート担当者は通常時は閲覧不可
- 調査時のみ対象テナントへのアクセスを許可
- アクセス理由を入力
- 操作を監査ログへ記録
- 一定時間で権限を失効
という仕組みを検討できます。
顧客企業から見ると、
「SaaS提供会社の誰でも自社データを見られる」
よりも、
「必要な担当者だけが記録を残してアクセスできる」
方が管理しやすくなります。
ユーザーの所属部署まで制御する場合
企業単位だけでは権限が足りないケースもあります。
例えば営業管理システムで、
営業1課は営業1課の案件のみ閲覧可能
営業部長は営業部全体を閲覧可能
経営層は全部署を閲覧可能
というルールです。
この場合、権限階層は、
テナント
↓
部署
↓
ユーザー
となります。
例えばデータに、
- tenant_id
- department_id
- owner_user_id
を持たせ、
「自分のデータだけ」
「所属部署のデータ」
「テナント全体」
を権限によって切り替える設計が考えられます。
RBACだけで足りない場合はABACも検討する
権限ルールが複雑になると、RBACだけでは表現しにくい場合があります。
例えば、
「営業担当者は自分が担当する案件だけ編集可能」
「部長は自部署の100万円以下の申請を承認可能」
といったルールです。
このようにユーザーやデータの属性を使って判定する方式を、ABAC(Attribute-Based Access Control)と呼びます。
例えば、
- ユーザーの部署
- ユーザーの役職
- 案件の担当者
- 申請金額
- データの状態
などを組み合わせます。
ただし、権限ルールを複雑にしすぎると運用が難しくなります。
まずRBACで整理し、必要な部分だけ条件を追加する方法が現実的です。
マルチテナントのDB設計には3つの代表パターンがある
企業ごとのデータ分離は、tenant_idだけが方法ではありません。
代表的には次の3つがあります。
1. 同じDB・同じテーブルを共有する
1つのテーブルに複数企業のデータを保存し、tenant_idで区別します。
例えば、
顧客テーブル
A社データ
B社データ
C社データ
が同じテーブルに存在します。
メリット
- 実装・運用を共通化しやすい
- テナント追加が容易
- インフラコストを抑えやすい
注意点
テナント条件の付け忘れが企業間データ漏えいにつながる可能性があります。
アプリケーション全体で一貫した制御が必要です。
2. 同じDBでスキーマを分ける
企業ごとにデータベーススキーマを分ける方式です。
例えば、
tenant_a.customers
tenant_b.customers
という構成です。
論理的な分離を強くできますが、テナント数が増えた場合のスキーマ管理やマイグレーションなどを考える必要があります。
3. 企業ごとにDBを分ける
企業ごとにデータベースそのものを分離します。
A社DB
B社DB
C社DB
という構成です。
データ分離を明確にしやすい一方、
- DB作成
- バックアップ
- マイグレーション
- 接続管理
- 監視
などの運用負荷が増えます。
どのデータ分離方式を選ぶべきか
「DBを分ければ正解」「tenant_id方式は危険」という単純な話ではありません。
例えば利用企業が数千社ある一般的なSaaSで、すべての企業へ専用DBを用意すると運用が複雑になる可能性があります。
一方、
- 大企業向け
- 機密情報を扱う
- 顧客ごとの分離要件が強い
- 大量データを扱う
という場合には、企業ごとのDB分離が適しているケースもあります。
検討項目としては、
- テナント数
- 1社あたりのデータ量
- セキュリティ要件
- 可用性
- バックアップ要件
- 顧客ごとのデータ移行
- 運用コスト
- カスタマイズ要件
などがあります。
Row Level Securityを使う方法
PostgreSQLなどでは、Row Level Security(RLS)を利用して行単位のアクセス制御を行う方法もあります。
例えばcustomersテーブルに複数テナントのデータがあったとしても、
「現在のテナントIDと一致する行だけアクセス可能」
というポリシーをデータベース側に設定できます。
アプリケーション側のWHERE条件だけに依存するより、データベース側でも防御できるため、設計によっては有効です。
ただし、RLSを導入すれば自動的に安全になるわけではありません。
- テナント情報をどのようにDBへ渡すか
- 管理者権限をどう扱うか
- バッチ処理をどうするか
- テストをどうするか
などを整理する必要があります。
アプリケーションとDBの両方で守る「多層防御」の一つとして検討するとよいでしょう。
APIでも必ずテナント・権限チェックを行う
画面上でボタンを非表示にするだけでは権限管理になりません。
例えば一般ユーザーには「削除」ボタンを表示しないようにしていたとします。
しかしAPIの、
DELETE /customers/1001
へ直接リクエストすれば削除できるのであれば、権限管理として不十分です。
そのためAPI側で、
- 認証済みか
- どのテナントに所属しているか
- 対象データはそのテナントのものか
- 削除権限を持っているか
を確認してから処理します。
画面の表示制御はユーザビリティ、サーバー側の認可処理がセキュリティ
と考えると分かりやすいでしょう。
フロントエンドとバックエンドの両方で権限を扱う
例えば管理者だけが利用できる「ユーザー追加」機能があるとします。
フロントエンドでは、
管理者 → ボタンを表示
一般ユーザー → ボタンを非表示
とできます。
しかしバックエンドでは別途、
「このユーザーにUSER_CREATE権限があるか」
を確認する必要があります。
フロントエンドだけでアクセス制御を完結させないことが重要です。
ファイルのテナント分離も忘れない
マルチテナント設計で見落とされやすいのが、データベース以外の情報です。
例えば、
- 契約書
- 顧客添付ファイル
- 商品画像
- CSV
- 帳票
などです。
DBでは正しくtenant_idを確認しているのに、ファイルURLを知っていれば他社ファイルをダウンロードできる設計では意味がありません。
例えばストレージ上でも、
tenant-a/documents/…
tenant-b/documents/…
のようにテナントを識別し、ファイル取得時に権限を確認する必要があります。
単純にファイル名を推測できないようにするだけではなく、アクセス時に認可することが重要です。
CSV・Excel出力にも権限管理が必要
SaaSでは、
「一覧画面では一部のデータしか見せていないが、CSV出力すると全件出てしまう」
という問題も起こり得ます。
例えば一般社員が自分の案件だけ閲覧できる場合、
画面:自分の案件だけ
CSV:テナント全体
となっていては権限設計が一貫していません。
- 画面
- API
- CSV
- Excel
- バッチ処理
まで同じ権限ルールを適用できるように設計します。
キャッシュにもtenant_idを含める
マルチテナントSaaSでは、キャッシュ設計にも注意が必要です。
例えば、
customer-list
というキーで顧客一覧をキャッシュするとします。
A社がアクセスしてA社の顧客一覧が保存されたあと、B社のアクセスでも同じキャッシュが返される設計では、企業間の情報漏えいにつながります。
そのため、
tenant:1:customer-list
tenant:2:customer-list
のように、テナントを識別できるキー設計を検討します。
キャッシュだけでなく、
- 検索インデックス
- 一時ファイル
- バックグラウンドジョブ
- レポート
- 通知処理
などでも、テナント境界が維持されているか確認します。
バックグラウンド処理でもテナント境界を維持する
例えば、
「毎晩、各企業の月次レポートを作成する」
というバッチ処理があるとします。
Webリクエストではログインユーザーからtenant_idを取得できますが、バックグラウンドジョブにはログインユーザーが存在しない場合があります。
そのためジョブ自体に、
- tenant_id
- 対象データ
- 実行権限
など必要なコンテキストを明示的に持たせます。
A社の処理中にB社のデータを取得しないことを、Web画面以外でも保証する必要があります。
監査ログにもtenant_idを持たせる
マルチテナントSaaSでは監査ログも企業ごとに分離することが重要です。
例えば、
- 誰が
- いつ
- 何を
- どのテナントで
- どのデータに対して
操作したのかを記録します。
監査ログには、
- tenant_id
- user_id
- action
- target_type
- target_id
- timestamp
などを持たせる方法があります。
これによって、
「A社の管理者が誰を追加したか」
といった履歴を確認できます。
当然ながら、A社管理者がB社の監査ログを閲覧できないようにする必要があります。
権限変更そのものも監査対象にする
特に重要なのが、ユーザー権限の変更履歴です。
例えば、
一般ユーザー
↓
管理者
へ変更された場合、
- 誰が変更したか
- いつ変更したか
- 誰の権限を変更したか
- 変更前の権限
- 変更後の権限
を記録しておくと調査しやすくなります。
権限管理システムそのものへの変更は、特に重要な監査対象です。
テナント削除時のデータをどう扱うか
SaaSでは契約解約もあります。
企業がサービスを解約した場合に、
- 即時削除するのか
- 一定期間保持するのか
- 論理削除するのか
- バックアップはいつ削除するのか
- 顧客へデータを返却するのか
を決めておく必要があります。
例えば、
解約
↓
ログイン禁止
↓
30日間データ保持
↓
完全削除
というルールも考えられます。
これは権限管理だけでなく、データライフサイクル設計にも関係します。
テナントの無効化とユーザーセッションを連動させる
例えば料金未払いなどによりテナントを停止したとします。
tenant.statusを「disabled」に変更しても、すでにログイン済みのユーザーがそのまま利用できてしまっては不十分です。
そのため、
テナント停止
↓
既存セッションを失効
↓
新規ログインを拒否
↓
APIアクセスも拒否
といった制御を検討します。
ユーザー単位だけでなく、企業単位のアカウント状態も認証・認可に反映させる必要があります。
マルチテナントの権限管理でよくある失敗
tenant_idのWHERE条件を付け忘れる
最も分かりやすい失敗です。
一部のAPIだけ、
「IDだけで検索する」
実装になってしまうと、別企業のデータへアクセスできる可能性があります。
共通化できる部分は、テナント条件を自動的に適用する仕組みにすることも検討します。
フロントエンドだけで権限を制御する
ボタンを非表示にしてもAPIへ直接アクセスされれば意味がありません。
バックエンドで必ず認可します。
tenant_idをユーザー入力から受け取る
利用者が自由にtenant_idを変更できる設計は避けます。
認証済みユーザーの所属情報からサーバー側で判定します。
管理者ならすべて見られるようにする
「admin」という1種類の権限だけを作ると、
テナント管理者とSaaS運営管理者の境界が曖昧になります。
管理者の種類と権限範囲を明確にします。
DBだけ分離して安心する
DBのデータが分かれていても、
- ファイル
- キャッシュ
- 検索
- CSV
- ログ
- バックアップ
から別企業の情報が漏れる可能性があります。
システム全体でテナント境界を考える必要があります。
権限ルールを複雑にしすぎる
細かい権限を大量に作ると、利用者も管理者も理解できなくなる場合があります。
例えば、
- 顧客閲覧
- 顧客追加
- 顧客編集
- 顧客削除
- 顧客CSV出力
- 顧客一括更新
などをすべて個別設定できるようにすると柔軟ですが、運用は複雑になります。
最初は役割を少数に整理し、本当に必要なところだけ細分化する方法が現実的です。
マルチテナント権限管理の設計手順
実際にSaaSを設計する場合は、次の順番で整理すると分かりやすくなります。
1. テナント単位を決める
まず、
「何を1テナントとするか」
を決めます。
例えば、
- 会社
- 店舗
- 学校
- 医療法人
- プロジェクト
などです。
2. ユーザーとテナントの関係を決める
次に、
- 1ユーザー1テナントか
- 1ユーザー複数テナントか
を決めます。
ここでDB構造が大きく変わることがあります。
3. データの所有単位を決める
例えば、
- 顧客
- 案件
- 契約
- 請求
- ファイル
のどれがテナント所有なのかを整理します。
4. ロールを決める
例えば、
- Owner
- Admin
- Member
- Viewer
などです。
5. 操作権限を整理する
各ロールについて、
- 閲覧
- 登録
- 編集
- 削除
- 出力
- 管理
のどこまで許可するかを決めます。
6. 部署・担当者単位の制御が必要か確認する
企業全体ではなく、
「自部署だけ」
「自分が担当するデータだけ」
という制御が必要か確認します。
7. SaaS運営者権限を整理する
運営会社が顧客データへアクセスする場合、
- 誰が
- どのような理由で
- どこまで
- どれくらいの時間
アクセスできるのかを決めます。
8. 監査ログを設計する
重要操作や権限変更を追跡できるようにします。
【コピペ用】マルチテナント権限管理チェックリスト
SaaSの権限設計を検討するときは、次の項目を確認できます。
テナント設計
- 何を1テナントとするか
- 1ユーザーは1テナントだけか
- 複数テナント所属はあるか
- テナント停止・削除のルールはあるか
データ分離
- 主要データにtenant_idがあるか
- SELECT時にテナントを確認しているか
- UPDATE時にテナントを確認しているか
- DELETE時にテナントを確認しているか
- tenant_idをクライアント入力だけに依存していないか
- ファイルもテナント分離されているか
- キャッシュもテナント分離されているか
- CSVや帳票もテナント分離されているか
権限
- テナント管理者を定義しているか
- 一般ユーザーを定義しているか
- 閲覧専用ユーザーが必要か
- SaaS運営管理者を分離しているか
- 部署単位の制御が必要か
- 担当者単位の制御が必要か
API
- API側で認証しているか
- API側でテナントを確認しているか
- API側で操作権限を確認しているか
- URLのID変更だけで他社データへアクセスできないか
運用
- 権限変更を監査ログへ記録するか
- 管理者操作を記録するか
- テナント停止時にセッションを失効するか
- 退会時のデータ削除ルールがあるか
- サポート担当者の顧客データアクセスを制限しているか
マルチテナントSaaSの権限管理に関するよくある質問
tenant_idをすべてのテーブルに付ける必要がありますか?
必ずしもすべてのテーブルに必要とは限りません。
例えば全テナント共通の都道府県マスタなどは、tenant_idを持たない場合があります。
一方、
- 顧客
- 案件
- 契約
- 注文
- ファイル
など企業ごとに所有されるデータでは、テナントを識別できる設計が必要です。
tenant_id方式でも安全なSaaSを作れますか?
適切に実装・テストすれば可能です。
重要なのは、すべての対象処理でテナント境界を強制することです。
アプリケーション側の認可に加えて、必要に応じてRow Level Securityなども検討できます。
企業ごとにDBを分ける方が安全ですか?
物理的な分離を強くできるメリットがあります。
一方、テナント数が増えるほどDB管理やマイグレーションなどの運用負荷も増えます。
SaaSの規模や顧客要件を踏まえて判断します。
テナント管理者は何でも操作できるようにしてよいですか?
システムによります。
例えば契約情報や監査ログなど、テナント管理者であっても編集できない方がよいデータがあります。
「管理者だから全権限」という前提ではなく、業務上必要な権限を整理することが重要です。
SaaS提供会社の管理者は顧客データを見られるようにするべきですか?
サポート上必要になる場合はありますが、常に全担当者が閲覧できる設計にする必要はありません。
対象テナントへの一時アクセス、アクセス理由、監査ログなどを組み合わせる方法があります。
権限管理は画面単位で設計すればよいですか?
画面単位だけでは不十分な場合があります。
例えば同じ顧客画面でも、
- 閲覧可能
- 編集可能
- 削除可能
では権限が異なります。
画面だけでなく「どのリソースに対して、どの操作を許可するか」を整理すると設計しやすくなります。
小規模SaaSでもマルチテナント設計は必要ですか?
複数の企業が同じシステムを利用するのであれば、利用企業が少なくてもデータ分離は必要です。
最初は3社でも、将来100社へ増える可能性があります。
後からテナント分離を追加するとDB設計や権限設計の変更が大きくなることがあるため、SaaSとして提供する予定なら初期段階から検討しておくことが重要です。
hiro-dev-labではマルチテナントSaaSの設計段階から相談できます
マルチテナントSaaSでは、単純にログイン機能を付けるだけでは十分ではありません。
実際には、
- テナント管理
- ユーザー管理
- 認証
- RBAC
- データ分離
- DB設計
- API認可
- ファイルアクセス制御
- 監査ログ
- セッション管理
などを組み合わせて設計する必要があります。
hiro-dev-labでは、
- SaaSの要求整理
- 要件定義
- テナント構造の整理
- ユーザー・ロール設計
- 権限設計
- データベース設計
- 認証・認可設計
- 監査ログ設計
- Webシステム開発
- 既存システムのSaaS化
など、構想段階から設計・開発まで相談できます。
例えば、
「1つのWebシステムを複数企業へ提供したい」
「企業ごとにデータを完全に分けたい」
「企業内でも管理者と一般ユーザーを分けたい」
「1ユーザーが複数企業を切り替えられるようにしたい」
「現在は単一企業向けだが、今後SaaSとして販売したい」
といった段階からでも整理できます。
マルチテナントSaaSの権限管理で最も重要なのは、単にroleを付けることではありません。
「どのテナントのデータなのか」と「そのテナント内で何をしてよいのか」をすべての処理で一貫して判定することが、企業ごとのデータを安全に分離するための基本です。