🔑

Supabaseのセキュリティ対策をまとめてみた

2023/01/20に公開約2,800字

概要

Supabase便利ですよね。
直感的にデータベースを触ることができ、データ操作が容易にできる印象を持たれています。認証も簡単に導入できるのは強みですね。

しかし、使い方を間違えると誰もが簡単にデータ操作ができます。他人が自分のデータを書き換えたりすることも可能なのです。

今回はSupabaseを使用する上で必要なセキュリティ対策についてまとめてみました。

セキュリティの重要性

JavaScriptを使用して認証、データ操作をするには anonkey(client key) が必要です。anonは匿名という意味合いがあります。

デフォルトでテーブルを作成した場合、anonkeyを使用して誰でもアクセスすることができてしまいます。
また、anonkey自体がクライアント側で使用する目的で使用されるので公開されるのは前提とされています。このままでは誰でもデータを取得、登録などできてしまいます。

そこでセキュリティ対策として2つほどやっておくべきことがあります。

テーブル権限変更

テーブルを作成するとデフォルトではanonkeyを使えば誰でもCRUD操作ができるようになります。

既存権限を確認する

まずは既存のロール、テーブル、何が許可されているかを確認しましょう。

PostgreSQLで以下のSQLを入力します。
もし何もしていなければ、granteeにanonがありCRUD操作が全て可能になっていることが確認できます。

select grantee, table_name, privilege_type
from information_schema.role_table_grants
where table_schema = 'public' order by grantee, table_name, privilege_type;

anonの権限を剥奪していきます。これで認証ユーザー以外がテーブルにアクセスすることができなくなります。(認証ユーザーしか操作できなくなる)

revoke all privileges on all tables in schema public from anon;

今後のデフォルト権限に備える

今後、テーブル作成時などにデフォルトで未認証ユーザーのアクセス権を無くすようにします。
Supabaseのデフォルト権限設定からanonの権限を全て取り除くようにします。テーブル、関数、シーケンスに関わる権限です。

alter default privileges in schema public revoke all on tables from anon;
alter default privileges in schema public revoke all on functions from anon;
alter default privileges in schema public revoke all on sequences from anon;

Row Level Security(RLS)

PostgresSQLを使用した行単位のセキュリティになります。
この機能を使用することで、認証ユーザーのみデータを作成、更新ができるといった制御が可能になります。

なお、例には公式の内容を使用しています。

https://supabase.com/docs/guides/auth/row-level-security

ポリシーを作成する

各テーブルにポリシーが関連づけられており、アクセスするたびポリシーが起動するようになります。

データ取得

例えば、ログインされたユーザーのデータのみ取得できるようにするには以下のようなSQLで実現可能です。

create policy "View own todos." 
    on todos for select 
    using ( auth.uid() = user_id );

フロント側からリクエストする際に以下のように変換されてデータ取得できるようになります。

select *
from todos
where auth.uid() = todos.user_id;

つまり、認証ユーザーIDと登録されているユーザーIDの整合性をみて、照合がとれたデータを持ってくるようになります。

データ更新

更新でも同じく、認証ユーザーIDと登録されているユーザーIDの整合性から同じであれば更新するようになります。

create policy "Register own todo."
  on todos for insert
  using ( auth.uid() = user_id );

データ削除

削除も更新と同じくです。

create policy "Delete own todo."
  on todos for delete
  using ( ( auth.uid() = user_id ) );

まとめて定義

実はまとめて自身のデータのみ全て操作できるように定義することができます。

create policy "Enable all actions for users based on user_id"
    on public.todos for all
    using ( auth.uid() = user_id )
    with check ( auth.uid() = user_id );

ヘルパー機能

RLSを作動させるためにはポリシーを作成する必要があります。
そのポリシーで使える便利な関数が存在します。

auth.uid()
auth.jwt()

いずれもログイン中のユーザーIDとJWTを取得できる関数になっています。

最後に

Supabaseは簡単に導入できますが、セキュリティ対策をしなければ危ういということが伝わったでしょうか。
この記事がみなさんのSupabaseライフの一助になれば幸いです。

参考記事

この記事作成にあたり、下記の記事には大変お世話になりました。感謝しております。

https://zenn.dev/suin/scraps/23d4355df14a42
https://zenn.dev/hrtk/scraps/ed6e10fc462393

Discussion

ログインするとコメントできます