BaaSとしてのSupabase
Supabaseは、単なるクラウドデータベースを提供しているだけではなく、postgRESTなどのバックエンドサービスを提供している。
postgRESTとは
postgRESTは、PostgreSQLのデータベーススキーマを読み取って、自動的にRESTful APIを生成するスタンドアロンのWebサーバーである。これによって、サーバ側でAPIコードを書かなくてもテーブルを追加するだけでエンドポイントが生える。
これは、フロントエンドやモバイル・ノーコードツールなどからデータベースを叩きたいときに有益な仕組みといえる。
テーブル: teams (id, name)
↓ 自動生成
GET /rest/v1/teams → 一覧取得
GET /rest/v1/teams?id=eq.1 → id=1のみ取得
POST /rest/v1/teams → 新規作成
PATCH /rest/v1/teams?id=eq.1 → 更新
DELETE /rest/v1/teams?id=eq.1 → 削除
クエリパラメータはそのままSQLのWHERE句や演算子に対応し、
GET /rest/v1/talents?select=first_name,last_name,teams(name)&team_id=eq.1
のようにすることで、外部キーをpostgRESTが自動検出し、SQLのJOINを書かなくてもselect=teams(name)の部分のようにネストした形でJOIN結果を取得できる。
ちょっと待って
では、このpostgRESTのwebサーバをanonキーで叩けたら誰でもデータを取れてしまうのか?
そんなことはない
postgRESTはリクエストのAuthorization: Bearer <JWT>を受け取ると、その中身のroleなどを見てPostgreSQL上でそのロールに切り替えてSQLを実行する。そのロールによってRLSでアクセス制御を設定できる。
RLSとは
RLSはRow Level Securityの略で、文字通り、テーブルに対する「行ごとの」アクセス制御を行う仕組みのこと。普通のDB権限管理では、テーブル単位でのアクセス制御しかできないが、RLSではさらに細かい行単位のポリシーで制御できる。
ポリシーとは
RLSが有効になったテーブルに対して「どの操作を、誰に、どんな条件で許可するか」を定義する個別のルールのこと。RLSが有効になった時点では、全アクセス拒否になっているので、ポリシーでアクセス権を追加していく。
create policy "ポリシー名"
on テーブル名
for select | insert | update | delete | all -- どの操作に適用するか
to ロール名 -- 誰に適用するか(authenticated, anon など)
using (条件式) -- 既存の行を対象にできるかの条件
with check (条件式); -- 書き込む行を許可するかの条件
バックエンドを自前で用意する場合
BaaSとしての機能を使わず(主にpostgRESTについて)、クラウドデータベースを使用したい場合、RLSの有効化が必須になる。というのも、先述した通り、postgRESTはデフォルトで有効になっておりanonキーで叩けてしまうため、RLSでpostgREST経由のアクセスを拒否する必要がある。このとき、RLSを有効化してポリシーを設定しないことで全拒否が実現できる。
バックエンドからデータベースへのアクセスは、テーブルオーナー権限のキーでアクセスすることでRLSの影響を受けずにデータを取れる。
まとめ
- PostgRESTは、DBスキーマからRESTful APIを自動生成する仕組みで、バックエンドを自前で書かなくてもフロントエンド・モバイル・ノーコードツールなどから直接DBを叩けるようにするための仕組み
- ただしPostgRESTはデフォルトで有効になっており、anonキーだけでもリクエスト自体は届いてしまう。それでも安全なのは、JWTの中身に応じてPostgreSQL側のロールが切り替わり、RLSによって行単位でアクセスが絞られるため
- RLSは有効化した時点でデフォルト全拒否となり、using(読む/対象にする条件)とwith check(書き込む内容の条件)を持つポリシーを追加することで、必要な範囲だけアクセスを許可していく
- PostgRESTを使わず、自前のバックエンドから直接DBに接続する構成を取る場合でも、PostgREST自体は有効なまま公開され続けているため、使用の有無に関係なく、RLSを有効化してポリシーを設定しないことで、この経路を明示的に塞いでおく必要がある
- 一方バックエンドは、テーブルオーナー権限(postgresロールなど)でDBに直接接続することで、RLSの制約を受けずにデータを自由に操作できる。つまり「PostgREST経由(anon/authenticated)は全拒否、バックエンド直接接続(postgres)はRLS対象外でフルアクセス」という2系統のアクセス経路を明確に分離するのが、この構成の肝である