1
1

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.

SolidJS で Supabase の Row Level Security を試してみた…の続き(補足)

Last updated at Posted at 2022-05-31

2022/12/6 追記:
GitHub リポジトリ側を supabase-js v2 にバージョンアップした関係で、リンク先のソースコードとは行番号や内容が噛み合わなくなっています。
(後日、この記事を supabase-js v2 に合わせて修正する予定)

先日、

の、

で、

  • 更新(UPDATE)時に以下をOR条件でチェック
    • 実行ユーザと投稿者(userid)の一致
    • 投稿の「他のユーザに許可する操作」が「3:読み取りと編集」
    • 実はこれらだけでは不十分ですが(userid列値を投稿者以外に上書き更新できるため)、今回は内容を簡単にするためにこのラインにとどめています

と記した点の補足です。

何が問題か?

先の箇条書きにもありますが、

  • 「他のユーザに許可する操作」(note_type)が「3:読み取りと編集」の投稿で、
  • 投稿者とは別のユーザが
  • userid列値やnote_type列値を上書きできる

ので、

  • 本来の投稿者ではなく他のユーザに投稿者が入れ替わってしまう
  • 本来の投稿者が上書き編集できなくなってしまう

事態が発生します。

そういう仕様のアプリケーションであれば問題はないのですが、先の記事のサンプルの仕様は、

  • 投稿者は(当初)投稿した本人の名前
  • 他ユーザに許可する権限の変更を他ユーザに認めていない

でしたので、

  • ブラウザの開発者ツールでコードを書き換えて実行される等

で、データを不正な状態に書き換えることができてしまう、といえます。

回避策

投稿者(userid)や「他のユーザに許可する操作」(note_type)が、投稿者以外に書き換えできないように何らかの形で制約を追加します。

一例として、

  • 投稿者(authors)テーブルを追加し、ここにarticlesテーブルと同じidおよびuseridの値を投稿時にINSERTする
  • id列でarticlesテーブルへの外部キー制約を設定する
  • authorsテーブルに追加の Row Level Security を設定する
  • articlesからの行DELETE直前に、対応するauthorsテーブルの行をDELETEする

方法を示します。

authors テーブル(投稿者を保管)

db-create.sql(80 行目〜)

authorsテーブルとRLS
create table authors (
  id bigint not null,
  updated_at timestamp with time zone,
  userid uuid not null,

  primary key (id)
);

alter table authors enable row level security;

create policy "Authenticated Users can view all article-authors."
  on authors for select
  using ( auth.role() = 'authenticated' );

create policy "Users can insert their own article-authors."
  on authors for insert
  with check ( auth.uid() = authors.userid );

create policy "Users can delete their own article-authors."
  on authors for delete
  using ( ( auth.uid() = authors.userid ) );
  • 参照(SELECT)は認証ユーザなら誰でも読める
  • 挿入(INSERT)時に実行ユーザと挿入する投稿者列(userid)の一致をチェック
  • 更新(UPDATE)は許可しない(記述なし)
  • 削除(DELETE)時に実行ユーザと対象行の投稿者列(userid)の一致をチェック

articles テーブル(投稿情報を保管)の Row Level Security 設定変更

db-create.sql(102 行目〜)

articlesテーブルのRLS変更
alter table authors add foreign key (id) references articles;

alter policy "Users can update their own articles or free-updatable articles."
  on articles
  using ( (
            ( auth.uid() = articles.userid ) or ( articles.note_type = 3 )
          ) and
          ( articles.userid = (
            select userid from authors where articles.id = authors.id)
          )
        );
  • 更新(UPDATE)時に以前の設定(『実行ユーザと投稿者(userid)の一致』および『投稿の「他のユーザに許可する操作」が「3:読み取りと編集」』のOR条件でチェック)とAND条件で以下をチェック
    • id列およびuserid列の値が一致するauthorsテーブル行の存在

これらを設定することで、

  • UPDATE時にuseridを変更する
  • UPDATE時にuseridのユーザ 以外note_typeを変更する

を防ぐことができます。

※アプリケーション側のコードについての説明は省略します。GitHub リポジトリで確認してください。

追加:note_typeの値の範囲をCHECK制約で制限する

Row Level Security とは別の話になりますが、アプリケーションの改ざんにより不正なデータが保存されるのを防ぐために、articlesテーブルにCHECK制約を追加します。

db-create.sql(114 行目〜)

articlesテーブルにCHECK制約追加
alter table articles
  add constraint note_type_range check (note_type between 1 and 3);

残った課題

これで一応対処はできるのですが、本来articlesテーブルへのデータ行挿入と、対応するauthorsテーブルへの行挿入、およびそれらの行削除は トランザクションで処理すべき です。

ところが、Supabase のクエリビルダには今のところトランザクションの機能がないので(何度か GitHub の Issue に上がって対処されずに Close されている模様?)、クエリビルダではなくストアドファンクションを使って RPC で実行する必要があります。

今回はここまでの対応は行いませんでした。

参考:

というわけで

フロントエンドのアプリケーションコードは悪意のあるユーザが書き換えて実行することができます。

Supabase で Database の自動生成 API を使って(ユーザを識別してデータアクセスする)ブラウザ向けアプリケーションを作るときは、Row Level Security でしっかりアクセス制限を掛けましょう。

おまけ:第 33 回 PostgreSQL アンカンファレンス@オンラインの発表資料

1
1
0

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
1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?