21
8

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

SupabaseのRow Level SecurityとFirebaseのセキュリティルール比較

Last updated at Posted at 2021-09-26

どうも、Supabase DevRelのタイラーです!

SupabaseはFirebaseと同じような感覚でサクッとアプリ開発入れる便利なツールとして親しまれていますが、皆さんはそんなRow Level Securityについてきちんと理解できていますか?Supabaseを使っている人の多くがFirebaseを過去に使っていた人たちということもあるので、今回はSupabaseのRow Level SecurityとFirebaseのセキュリティルールの違いを一つ紹介できたらなと思います。

その前にRow Level Securityのおさらい

Row Level SecurityとはSupabaseに組み込まれた(正確にはPostgreSQLに組み込まれた)アクセス制限の仕組みで、特定のrowにどのユーザーがアクセスすることができるのかをSQLを使って設定することができる形になっています。Firebaseでいうセキュリティルールにあたるものですね。

例えば、postsというテーブルがあったときに、「誰でも閲覧はできるが、投稿者しかデータを更新・削除できない」といった制限をSQLを使ってかけることができる代物になっています。

こんな感じのpostsテーブルがあった場合に:

id user_id content
1 aaa 夏は暑いね
2 bbb 猫はかわいいね
3 ccc 神奈川は最高だね

このような設定をすれば「誰でも閲覧はできるが、投稿者しかデータを更新・削除できない」という制限をかけることができる。

-- 投稿は誰でも閲覧できる
create policy "Posts are viewable by everyone."
  on posts for select using (
    true
  );

-- 他人になりすまして投稿することはできない
create policy "Posts can be created under your user_id"
  on posts for insert with check (
    auth.uid() = user_id
  );

-- 投稿主しか投稿の更新ができない
create policy "Posts can be updated by the creator"
  on posts update using (
    auth.uid() = id
  ) with check (
    auth.uid() = id
  );

-- 投稿主しか投稿の削除ができない
create policy "Posts can be deleted by the creator"
  on posts delete using (
    auth.uid() = id
  );

この際with checkusingというキーワードが出てきましたね。with checkは新しく作成されるデータを指していて、insertupdateをする際に更新後のデータを使って制限をかけることができます。usingは今テーブルに既に保存されているデータを参照し権限設定ができる形になっております。

Firebaseとの違いはフィルターとして使えるか使えないか

SupabaseとFirebaseのデータベースは両方ユーザーからのアクセスを制限し、セキュアなアプリケーションを作るための機能が備わっています。ただ、両者の違いはそのセキュリティ機能がフィルターとして機能してくれるか否かです。

Firebaseのセキュリティルールはフィルターになりません。このことについてはFirebaseチームのDoug Stevenさんもこちらで語っています。

例えばこのようなセキュリティルールがあったときに

match /privatePosts/{postId} {
  allow read: if request.auth.uid == resource.data.userId;
}

下記のようにprivatePostsコレクションから全データを引っ張ってこようとするクエリーを投げます。

const q = query(collection(db, "privatePosts"));

const querySnapshot = await getDocs(q);

そうするとこのクエリーはセキュリティルールがアクセスを弾いてエラーになっちゃうんですね。エラーが出る理由は、上記のようにコレクション全体に対して取得リクエストを投げるとuserIdが自分と一致していないものもあるからです。これが「Firebaseのセキュリティルールはフィルターとしては使えない」というFirebaseのセキュリティルールの性質です。今回はFirestoreのセキュリティルールを例に出しましたが、Realtime Databaseも同じ性質を持っています。

対してSupabaseの方はどうでしょう?

例えばprivate_postsというテーブルがあり、そのテーブルにこのようなRow Level Securityルールがかけられていたとしましょう:

-- 投稿主でないと閲覧できない
create policy "Private posts are viewable by everyone."
  on private_posts for select using (
    auth.uid() = user_id
  );

このような制限がかかっているprivate_postsテーブルに対してSupabaseを使ってこのようなprivate_postsのデータ全てを取得するクエリーを投げてみます。その際仮に今SupabaseにログインしているユーザーのユーザーIDがmy_idだったとしましょう。

const { data, error } = await supabase.from('private_posts').select()

するとこんな感じのデータが返ってきます。

[
  {
    id: 3,
    user_id: 'my_id',
    content: '秘密のコンテンツ'
  },
  {
    id: 5,
    user_id: 'my_id',
    content: '見られたくない秘密'
  },
  {
    id: 9,
    user_id: 'my_id',
    content: '隠したい過去'
  }
]

見ていただきたいのは、返ってくるデータのuser_idが全てmy_idになっている点です。クエリー事態は全データを取得しに行ったのに、Row Level SecurityがFilterの役割を果たし、自動でuser_idに対してwhereがかかったような形でデータを取得することができました。これがSupabase(PostgreSQL)のRow Level SecurityのFilterとして使える特性です!

まとめ

Firebaseはクライアント側でセキュリティルールとマッチしたwhere文を書かないとクエリーできないのに対してSupabaseはセキュリティルールさえ組んでおけば後は深く気にすることなくデータをクエリーするだけでユーザーが閲覧権限を持つデータを引っ張ってくることができます。地味なことかもですが、クライアント側で実装ミスをしても想定通り挙動してくれたりとメリットはちょいちょいあるのかなと思っています!

21
8
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
21
8

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?