データベースのユーザーと権限管理

データベースのユーザーと権限管理とは

アプリが [[リレーショナルデータベース]] に接続するとき、設定ファイルにこんな文字列を書きます。

postgres://app_user:s3cret@db.example.com:5432/shop
            ^^^^^^^^
            これがDBの「ユーザー」

初学者が見落としがちなのが、この app_user の部分です。データベースはOSと同じように「ユーザー」という概念を持っていて、接続してきた相手が誰かを認証し、そのユーザーに許された操作しか実行しません

つまり「DBに接続できた」と「何でもできる」はまったく別です。同じ SELECT * FROM orders でも、あるユーザーでは結果が返り、別のユーザーでは permission denied for table orders で拒否されます。権限管理とは、この「誰として繋ぐか」を用途ごとに設計する作業のことです。

GRANTとREVOKE

権限は「誰に」「どのオブジェクトに」「どの操作を」の3点セットで指定します。付与が GRANT、取り消しが REVOKE です。

CREATE USER app_user WITH PASSWORD 's3cret';

-- ordersテーブルに対して読み書きだけ許可(削除は含めない)
GRANT SELECT, INSERT, UPDATE ON orders TO app_user;

-- 誤って付けた権限を取り上げる
REVOKE DELETE ON orders FROM app_user;

操作の種類は、おおまかに次のように分かれます。

分類代表的な権限意味
データ操作SELECT / INSERT / UPDATE / DELETE行を読む・追加する・書き換える・消す
定義変更CREATE / ALTER / DROPテーブルを作る・定義を変える・丸ごと消す
管理ユーザー作成・権限付与・設定変更DB全体の運営に関わる操作

つまずきやすいのが「新しく作ったテーブルには権限が自動で付かない」点です。[[スキーママイグレーション]] で CREATE TABLE した直後に GRANT を書き忘れると、開発環境(たいてい強い権限で動かしている)では動くのに本番だけ permission denied になります。PostgreSQL の ALTER DEFAULT PRIVILEGES のように「今後作られるテーブルにも既定で権限を付ける」設定を先に入れておくのが定石です。

ロールで権限を束ねる

ユーザーが増えるたびに個別に GRANT を打っていると、すぐに管理できなくなります。そこで権限を ロール(役割)にまとめ、ユーザーにはロールを与えます。

CREATE ROLE readonly;
GRANT SELECT ON ALL TABLES IN SCHEMA public TO readonly;

GRANT readonly TO analytics_user;   -- 分析担当はこれ1行で完了

[[最小権限の原則]] で説明されている RBAC を、データベースの世界で実現したものがこれです。なお PostgreSQL ではユーザーとロールは同じ実体で、ログイン属性の有無で呼び分けているだけです。

用途別にアカウントを分ける

実務では、1つのシステムに対して複数のDBアカウントを用意します。

アカウント与える権限使う場面
アプリ用必要なテーブルへの SELECT / INSERT / UPDATE(必要なら DELETE)Webアプリの常時接続
参照専用SELECT のみ管理画面の集計、障害調査、BIツール
マイグレーション用CREATE / ALTER / DROP などの定義変更デプロイ時のスキーマ適用のみ
管理者ほぼ全部初期構築・障害対応を人が手で行うとき

分ける単位は「別のユーザー+別の接続文字列」です。接続情報が増えるので、[[シークレット管理]] の仕組みに載せて配布します。

この分割の効き目は具体的です。アプリ用アカウントに DROP 権限がなければ、[[SQLインジェクション]] を突破されても攻撃者はテーブルを消せません。マイグレーション用の強い権限は、デプロイの数十秒間しか使われないので、漏れたときの露出時間も短くなります。

スーパーユーザーで動かす危険

開発中に権限エラーが出て、面倒だからと postgresroot といったスーパーユーザーで接続し、そのまま本番へ——これが最も多い事故のパターンです。

  • 侵入されたとき、全テーブルの読み書きどころかユーザーの追加や設定変更までできてしまう
  • [[Row Level Security(RLS)]] はテーブル所有者とスーパーユーザーをバイパスします。ポリシーを丁寧に書いても、繋いでいるユーザーが強すぎれば無効になる
  • DELETE FROM users; のような操作ミスを、DB側の権限で止められない

自分のアプリがどのユーザーで繋いでいるかは SELECT current_user; で確認できます。「接続できたから正常」で終わらせず、そのユーザーで DROP TABLE が通ってしまわないかを一度試してください。

初学者向けポイント

  • ローカル環境で参照専用ユーザーを1つ作り、そのユーザーで UPDATE が弾かれる様子を実際に見ておくと理解が一気に進みます
  • permission denied はバグではなく設計どおりの拒否であることが多い。権限を足す前に「その操作は本当にアプリに必要か」を考える
  • [[コネクションプーリング]] のプールは同じユーザーの接続を束ねる仕組みです。用途別にアカウントを分けるなら、プールも用途ごとに分かれます

関連技術とのつながり

  • [[最小権限の原則]] — この記事はその原則をデータベース層で実装する具体策
  • [[SQLインジェクション]] — 突破されたときの被害範囲を決めるのが接続ユーザーの権限
  • [[Row Level Security(RLS)]] — 行単位の制御。ただし強すぎるユーザーではバイパスされる
  • [[スキーママイグレーション]] — 定義変更用の強い権限を、適用時だけ使う運用
  • [[リレーショナルデータベース]] — ユーザー・ロール・権限を持つ本体
Q: データベースの接続文字列に含まれるユーザー名は何を決めている?
- [ ] 接続の暗号化方式
- [x] そのユーザーに許された操作しか実行できないという権限の範囲
- [ ] クエリの実行速度の上限
解説: DBは接続してきた相手を認証し、そのユーザーに許された操作だけを実行します。「接続できた=何でもできる」ではありません。

Q: アプリ用のDBアカウントとは別に、マイグレーション用のアカウントを用意する理由はどれ?
- [x] CREATE / ALTER / DROP といった定義変更の強い権限を、適用時だけに限定するため
- [ ] マイグレーションの実行速度を上げるため
- [ ] アプリの接続数を増やすため
解説: 常時稼働するアプリに定義変更権限を持たせないことで、侵入や不具合が起きても構造を壊されなくなります。

Q: スーパーユーザーでアプリを接続する危険として本文が挙げているのはどれ?
- [ ] クエリのキャッシュが効かなくなる
- [ ] 接続プールが使えなくなる
- [x] Row Level Security のポリシーがバイパスされて効かなくなる
解説: RLSはテーブル所有者やスーパーユーザーをバイパスするため、ポリシーを書いても接続ユーザーが強すぎると無効になります。