メインコンテンツまでスキップ

行単位セキュリティー

細かな認証ルールが必要な場合、PostgreSQLの行単位セキュリティー(Row Level Security - RLS)に勝るものはありません。

ポリシーはPostgreSQLのルール・エンジンです。非常に強力で柔軟性があり、固有のビジネス・ニーズに合った複雑なSQLルールを書くことができます。

ポリシー

ポリシーはコツをつかめば簡単に理解できます。各ポリシーはテーブルに関連付けられており、テーブルにアクセスするたびポリシーが実行されます。ポリシーは、すべての問い合わせにWHERE句を追加するようなものだと考えることができます。例えば、次のようなポリシーがあります。

create policy "Individuals can view their own todos."
on todos for select
using ( auth.uid() = user_id );

これは、ユーザーがtodosテーブルから選択しようとするたびに、このように変換されます。

select *
from todos
where auth.uid() = todos.user_id; -- ポリシーが暗黙の内に追加されます。

ヘルパー関数

Supabaseには、ポリシーで使用できる簡単な関数がいくつか用意されています。

auth.uid()

リクエストを行ったユーザーのIDを返します。

auth.role()

caution

非推奨

auth.role()関数は非推奨となり、PostgresでネイティブにサポートされているTOフィールドを使用するようになりました。

-- 非推奨
create policy "Public profiles are viewable by everyone."
on profiles for select using (
auth.role() = 'authenticated' or auth.role() = 'anon'
);

-- 推奨
create policy "Public profiles are viewable by everyone."
on profiles for select
to authenticated, anon
using (
true
);

auth.email()

リクエストを行ったユーザーのメール・アドレスを返します。

以下に、PostgreSQLのRLSの能力を示す例を示します。

読み取りアクセスの許可

-- 1. テーブルの作成
create table profiles (
id uuid references auth.users,
avatar_url text
);

-- 2. RLSを有効化
alter table profiles
enable row level security;

-- 3. ポリシーを作成
create policy "Public profiles are viewable by everyone."
on profiles for select using (
true
);
  1. パブリック・スキーマ(デフォルトスキーマ)にprofilesというテーブルを作成します。
  2. 行単位セキュリティーを有効にします。
  3. すべてのselectクエリの実行を許可するポリシーを作成します。

更新の制限

-- 1. テーブルを作成
create table profiles (
id uuid references auth.users,
avatar_url text
);

-- 2. RLSを有効化
alter table profiles
enable row level security;

-- 3. ポリシーを作成
create policy "Users can update their own profiles."
on profiles for update using (
auth.uid() = id
);
  1. パブリック・スキーマ(デフォルト・スキーマ)にprofilesというテーブルを作成します。
  2. RLSを有効にします。
  3. ログインしたユーザーが自分のデータを更新することを許可するポリシーを作成します。

Only anon or authenticated access

You can add a Postgres role

create policy "Public profiles are viewable by everyone."
on profiles for select
to authenticated, anon
using (
true
);

結合を含むポリシー

ポリシーには、テーブルの結合を含めることもできます。この例では、より高度なルールを構築するために、「外部」のテーブルにクエリを実行する方法を示しています。

create table teams (
id serial primary key,
name text
);

-- 2. 多対多の結合を作成
create table members (
team_id bigint references teams,
user_id uuid references auth.users
);

-- 3. RLSを有効化
alter table teams
enable row level security;

-- 4. ポリシーを作成
create policy "Team members can update team details if they belong to the team."
on teams
for update using (
auth.uid() in (
select user_id from members
where team_id = id
)
);

注意: RLSがmembersに対しても有効な場合、ユーザーはmembersに対する読み取り(select)アクセス権も持っていなければなりません。そうでなければ、結合されたクエリは何の結果も得られません。

security definer関数を使ったポリシー

ポリシーには、security definer関数も使用できます。これは、多対多の関係で、リンク先のテーブルへのアクセスを制限したい場合に便利です。この例では、上記のteamsmembersの例に続いて、security definer関数をポリシーと組み合わせて使用し、membersテーブルへのアクセスを制御する方法を示します。

-- 1. 上記の「結合を含むポリシー」の例に従います

-- 2. RLSを有効化
alter table members
enable row level security

-- 3. security definer関数を作成
create or replace function get_teams_for_authenticated_user()
returns setof bigint
language sql
security definer
set search_path = public
stable
as $$
select team_id
from members
where user_id = auth.uid()
$$;

-- 4. ポリシーを作成
create policy "Team members can update team members if they belong to the team."
on members
for all using (
team_id in (
select get_teams_for_authenticated_user()
)
);

メール・アドレスのドメインを検証

Postgresには、文字列の右端n文字を返す関数right(string, n)があります。これを使用して、スタッフのメール・アドレスのドメインを照合できます。

-- 1. テーブルを作成
create table leaderboard (
id uuid references auth.users,
high_score bigint
);

-- 2. RLSを有効化
alter table leaderboard
enable row level security;

-- 3. ポリシーを作成
create policy "Only Blizzard staff can update leaderboard"
on leaderboard
for update using (
right(auth.email(), 13) = '@blizzard.com'
);

行のTTL(Time to live - 生存時間)

InstagramのストリーズやSnapchatで見られるTTL(Time To Live)機能を実装するためにも利用できます。 次の例では、storiesテーブルの行は、過去24時間以内に作成された場合にのみ利用可能です。

-- 1. テーブルを作成
create table if not exists stories (
id uuid not null primary key DEFAULT uuid_generate_v4(),
created_at timestamp with time zone default timezone('utc' :: text, now()) not null,
content text not null
);

-- 2. RLSを有効化
alter table stories
enable row level security;

-- 3. ポリシーを作成
create policy "Stories are live for a day"
on stories
for select using (
created_at > (current_timestamp - interval '1 day')
);

高度なポリシー

SQLの機能をフルに活用して、極めて高度なルールを構築ができます。

この例では、postsテーブルとcommentsテーブルを作成し、別のポリシーに依存するポリシーを作成します(この場合、commentsポリシーはpostsポリシーに依存します)。

create table posts (
id serial primary key,
creator_id uuid not null references auth.users(id),
title text not null,
body text not null,
publish_date date not null default now(),
audience uuid[] null -- many to many table omitted for brevity
);

create table comments (
id serial primary key,
post_id int not null references posts(id) on delete cascade,
user_id uuid not null references auth.users(id),
body text not null,
comment_date date not null default now()
);

create policy "Creator can see their own posts"
on posts
for select
using (
auth.uid() = posts.creator_id
);

create policy "Logged in users can see the posts if they belong to the post 'audience'."
on posts
for select
using (
auth.uid() = any (posts.audience)
);

create policy "Users can see all comments for posts they have access to."
on comments
for select
using (
exists (
select 1 from posts
where posts.id = comments.post_id
)
);

Tips

データベースのテーブルのリアルタムを有効化

Realtime Serverは、行単位セキュリティー(RLS)ポリシーに応じて、データベースの変更を許可されたユーザーにブロードキャストします。 パブリケーションに追加するテーブルでは、行単位セキュリティーを有効にして行セキュリティーポリシーを設定することをお勧めします。 ただし、テーブルでRLSを無効にして、接続しているすべてのクライアントに変更をブロードキャストもできます。

/**
* リアルタイム・サブスクリプション
* 公開されたテーブルでのみ、リアルタイムでのリスニングを許可する。
*/

begin;
-- リアルタイムのパブリケーションを削除
drop publication if exists supabase_realtime;

-- パブリケーションを再作成しますが、どのテーブルに対しても有効にしません
create publication supabase_realtime;
commit;

-- パブリケーションにテーブルを追加
alter publication supabase_realtime add table products;

-- パブリケーションに他のテーブルを追加
alter publication supabase_realtime add table posts;

ポリシーを使用する必要はありません

他のバックエンド <-> ミドルウェア <-> フロントエンドのアーキテクチャでセキュリティ・ルールを作成するのと同じように、ミドルウェアに認証ルールを置くこともできます。

ポリシーはツールです。「サーバーレス/Jamstack」による構成の場合は、ミドルウェアを一切導入する必要がないので、特に効果的です。

しかし、アプリケーションに別の認証方法を使用したい場合は、それも問題ありません。Supabaseは「単なるPostgres」ですので、Postgresで動作するアプリケーションであれば、Supabaseでも動作します。

Tips:すべてのテーブルに対してRLSを有効化して、テーブルへアクセスできないようにしてください。その上で、RLSをバイパスするように設計された私たちの「サービス」をご利用ください。

クライアントでサービス・キーを使用しない

Supabaseは、RLSを回避するための特別な「サービス」キーを提供しています。 これらのキーは、決してブラウザーで使用したり、ユーザーに公開したりしてはいけませんが、管理作業には便利です。