アプリにロールベースのアクセス制御(RBAC)を追加する方法
ロールベースのアクセス制御を追加するには、各利用者に1つ以上のロール(例:管理者、編集者、閲覧者)を割り当て、各ロールが実行できる操作を定義し、サーバー上ですべてのリクエストについてその権限を確認します。ボタンを隠すだけでは不十分です。アクセスするレコードが、利用者のアカウントまたはチームに属しているかも確認してください。最初はデータベースに少数の固定ロールを作り、別の利用者のデータに直接アクセスを試みることでテストしてください。
認証と認可
混同されやすい2つの言葉があります。
- 認証は、*あなたは誰か?*という問いです。ログインのことです。アプリにログインを追加する方法を参照してください。
- 認可は、*あなたは何をしてよいか?*という問いです。ログインした利用者がどのページ、レコード、操作にアクセスできるかを指します。
RBACは、認可を実現する最も一般的な方法です。重要なのは、2026年9月時点で Broken Access Control(アクセス制御の不備)が、OWASP Top 10:2025で第1位になっていることです。OWASP Top 10は、広く使われているWebアプリケーションの重大なセキュリティリスクの一覧です。典型的な失敗例は、URLのIDを書き換えて他の利用者のレコードを読んでしまうことや、一般の利用者が管理者専用のAPIを呼び出してしまうことです。
モデルを選ぶ
| モデル | 仕組み | 向いている用途 | トレードオフ |
|---|---|---|---|
| 単一のロール列 | users.role = admin / member | ロールが2〜3種類の小さなアプリ | 場所によって異なるロールが必要になると、扱いが複雑になる |
| ロールと権限 | ロールを名前付きの権限(invoices.approve)に対応させ、コードで権限を確認する | ルールが増えていくアプリ | テーブルが増え、権限の一覧を保守する必要がある |
| チームごとのロール(マルチテナント) | 利用者、組織、ロールを持つメンバーシップテーブル | 人がチームやワークスペースに所属するSaaS | すべてのクエリを組織ごとに限定する必要がある |
| 属性ベースまたは関係ベース | レコードの属性(「作成者は自分の投稿を編集できる」)や、関係(共有されている)に基づくルール | ドキュメント、共有、複雑なワークフロー | 理解が難しく、専用のライブラリやサービスの検討が必要 |
| 認証プロバイダーのロール | ログインプロバイダーがロールや組織の所属を保存する | すでにホスト型の認証プロバイダーを使っているアプリ | そのプロバイダーに依存する。それでもサーバーで強制する必要がある |
実際には、多くのアプリは2つのものを組み合わせます。ロールは、どの種類の操作ができるかを決め、所有権は、どのレコードに対してその操作をできるかを決めます。「編集者は投稿を編集できる」はロールのルールであり、「…自分のワークスペース内の投稿」は所有権のルールです。どちらも必要です。
あなたが思うより、シンプルに始めてください。管理者、メンバー、閲覧者の3つで足りるアプリは意外に多いものです。ロールは後から分けるほうが、誰も理解できない10のロールを解きほぐすより簡単です。
コードを書く前にロールを設計する
最初に、権限の表を平易な言葉で書いてください。これは、AIへの仕様であると同時に、テスト計画にもなります。
| 操作 | オーナー | 管理者 | メンバー | 閲覧者 |
|---|---|---|---|---|
| チーム内のプロジェクトを表示する | ✓ | ✓ | ✓ | ✓ |
| プロジェクトを作成・編集する | ✓ | ✓ | ✓ | |
| プロジェクトを削除する | ✓ | ✓ | ||
| メンバーを招待・削除する | ✓ | ✓ | ||
| ロールを変更する | ✓ | |||
| 請求 | ✓ |
例外的なケースも決めておきましょう。最後のオーナーは退出できるのか?管理者はオーナーを削除できるのか?削除された利用者には何が見えるのか?
ステップごとの進め方
1. まず正常に動くログインを用意する
RBACは認証の上に成り立ちます。サーバーは、セッションまたは検証済みのトークンから、各リクエストを誰が送っているかを確実に把握していなければなりません。ブラウザがリクエストボディで送ってきたユーザーIDを信用してはいけません。
2. ロールをデータベースに保存する
単一のチームのアプリなら、利用者テーブルの role 列で十分です。チームがある場合は、メンバーシップのテーブルを使います。
create table memberships (
user_id uuid references users(id),
organization_id uuid references organizations(id),
role text not null check (role in ('owner','admin','member','viewer')),
primary key (user_id, organization_id)
);
通常のプロフィール更新で、利用者が自分のロールを設定できてはいけません。ロールの変更は、専用の、権限チェック付きのエンドポイントを通すようにしてください。
3. 権限チェックはサーバー上の一か所にまとめる
can(user, 'projects.delete', project) のような関数を1つ書き、データを読み取ったり変更したりするすべてのAPIルート、サーバーアクション、バックグラウンドタスクから呼び出してください。中央の関数が1つあれば、コード中に散らばったチェックよりも、レビューやテストがしやすくなります。デフォルトで拒否してください。どのルールも許可していない操作は、拒否されます。
4. すべてのクエリを限定する
チームのデータを扱うすべてのクエリには、チームの条件を含めてください。where id = :id だけでなく、where organization_id = :currentOrg とします。これにより、/projects/123 を /projects/124 に書き換えて、別の会社のプロジェクトを見られてしまうことを防げます。別のチームのレコードには、「禁止」ではなく「見つからない」を返してください。そうすれば、そのレコードが存在することも明かしません。
5. 重要な箇所にはデータベースレベルの保護を加える
Postgresの**行レベルセキュリティ(RLS)**を使うと、どの行をどの利用者が見られるかを、データベース自身に強制させられます。ブラウザがデータベースに直接接続する場合(一部のホスト型バックエンドのように)には不可欠です。それ以外の場合でも、機密性の高いテーブルに対する追加の防御層として役立ちます。
6. UIにロールを反映する
利用者ができないことを隠したり無効にしたりして、アプリが混乱しないようにします。これは使いやすさのためだけのものです。実際の保護は、サーバー側のチェックが担います。
7. 重要な操作を記録する
ロールの変更、データの削除、レコードのエクスポートを誰がいつ行ったかを記録します。監査ログは、サポートへの問い合わせやインシデントの対応に役立ちます。
8. 攻撃者の視点でテストする
各ロールでログインし、権限表のすべての操作を試します。その後、うまくいってはいけないことも試してください。閲覧者として管理者向けのAPIルートを呼び出す、リクエストやURLのIDを書き換える、最後のオーナーとして自分を削除する、などです。ルールが固まったら、権限表に対する自動テストを書く価値があります。
効果のあるプロンプト
ロールベースのアクセス制御を追加してください。組織ごとのロールは、owner、admin、member、viewer とし、メンバーシップのテーブルに保存します。権限は次の表のとおりです:[表を貼り付け]。サーバー側に、can(user, action, resource) という1つのヘルパーを作ってください。デフォルトで拒否し、すべてのAPIルートで使ってください。すべてのクエリは organization_id で限定してください。利用者ができない操作はUIで隠してください。ただし、セキュリティをその非表示に頼らないでください。
このアプリのすべてのAPIルートとサーバー関数をレビューしてください。それぞれについて、どのデータを読み取るか、変更するか、利用者がログインしているか確認しているか、ロールを確認しているか、クエリを組織で限定しているかを一覧にしてください。確認が欠けている箇所は修正し、その一覧を見せてください。
管理者専用の /admin ページを追加してください。オーナーがメンバーのロールを変更できるようにします。最後のオーナーを削除したり降格させたりすることは防いでください。すべてのロール変更は、誰がいつ行ったかとともに記録してください。ロール変更のエンドポイントは、オーナーでない人を拒否するようにしてください。
よくある間違い
- ロールをフロントエンドでだけ確認する。 APIは、呼び出せる誰に対しても開いたままです。
- クライアントから送られたIDを信用する。 セッションではなく、リクエストボディの
userIdやroleを使ってしまう。 - 限定されていないクエリ。 レコードIDだけで取得している。
- ページではロールを確認するが、APIルート、エクスポート、ファイルのダウンロード、Webhookでは確認しない。
- メールアドレスで管理者を判定する。 コード中に
if email === 'me@...'のように書かれている。忘れやすく、変更も困難です。 - 早すぎる多数のロール。 半年後には、誰にも説明できなくなります。
- 古いセッション。 降格された利用者が、ログアウトするまで管理者の権限を持ち続ける。機密性の高い操作では、データベースからロールを確認するか、ロールを更新してください。
RBACのチェックリスト
- 権限表を平易な言葉で書き、例外的なケースを決めた
- サーバーは、リクエストのデータではなく、セッションから利用者を特定している
- ロールはデータベースに保存され、利用者が自分のロールを変更できない
- 権限チェックは一か所にまとまっており、デフォルトで拒否し、すべてのルートで使われている
- すべてのクエリが、利用者の組織または所有権で限定されている
- ブラウザがデータベースに到達できる場合は、行レベルセキュリティを設定している
- UIは、利用者ができないことを隠している(使いやすさのためだけ)
- 重要な操作が記録されている
- すべてのロールを、直接のAPI呼び出しやIDの書き換えを含めてテストした
アクセス制御は、より長いリストの1項目にすぎません。残りについては、AIで作ったアプリのセキュリティチェックリストを参照してください。社内スタッフ向けのツールを作る場合は、社内ツールの作り方で、ロールをどこに置くかを説明しています。
MythexでのRBAC
Mythexは、アプリの利用者向けの内蔵のログインやロールの仕組みを提供していません。認証ライブラリまたはプロバイダーを選び、ロールを説明すると、エージェントがそれをコードとプロジェクトのPostgresデータベースに組み込みます(アプリにログインを追加する)。Mythex独自のワークスペースのロール(オーナーやメンバーなど)は、Mythexのプロジェクトを編集できる人を制御するもので、公開したアプリを使える人を制御するものではありません。構築の前にプランモードで権限表を合意し、そのあとで上記のレビュー用プロンプトを使い、公開前にプレビューですべてのロールをテストしてください。ドキュメントのアプリのセキュリティ用プロンプトも、次の確認に役立ちます。
よくある質問
ロールベースのアクセス制御とは何ですか?
ロールベースのアクセス制御(RBAC)は、利用者が持つロール(管理者、編集者、閲覧者など)に基づいて、各利用者ができることを決める仕組みです。権限は個人ではなくロールに紐づけるため、誰かができることを変えたいときは、そのロールを変更するだけで済みます。
認証と認可の違いは何ですか?
認証は、その人が誰かを確認することで、通常はログインによって行います。認可は、ログインした本人が何を見て、何をできるかを決めることです。RBACは認可を実現する方法の一つです。
画面のボタンを隠すだけでアクセスを制限できますか?
いいえ、できません。誰でもAPIを直接呼び出せるため、ボタンを隠しても見え方が変わるだけです。データを読み取ったり変更したりするすべてのリクエストは、サーバー上で、利用者のロールとそのレコードの所有者を照らして確認しなければなりません。
PostgreSQLの行レベルセキュリティを使うべきですか?
行レベルセキュリティを使うと、どの行を誰が読み書きできるかを、データベース自身が強制できます。これは強力な安全網です。特に、フロントエンドがデータベースに直接接続する場合に有効です。ただし、複雑さが増すため、多くのアプリはサーバーのコードで権限を強制し、機密性の高いテーブルにだけ行レベルセキュリティを追加の防御層として使います。