🍆

【注意喚起】Supabase初心者向け|住所などの重要なデータを扱うときはSELECT文に気をつけろ!

2024/04/27に公開

SupabaseがGA(正式リリース)となったり、PlanetScaleの有料化などが重なり、ここ1ヶ月ぐらいで急速にSupabaseが注目を集めているように感じます。

Supabaseはフロントエンドから直接DBのデータを呼び出すような感覚で利用できる新感覚の BaaS です。であるが故にノウハウが溜まっておらずちょっとしたミスが重大な事件に繋がりかねないので、注意喚起としてこの記事を書いています。

実際に見つけたとある事象をご紹介します。

とあるAIツールで他ユーザーが入力したプロンプトやメアドが丸見えになっていた事件

日本向けに展開された無料AIツールで、ChatGPTが無料で無制限に使い放題という大盤振る舞いで数万ほどのユーザーを抱えていた大人気のAIサービスです。Supabaseが利用されていました。(サービス名は伏せておきますが海外発とだけ。問題は既に報告済みで翌日には解消されました。)

このサービスではフロントエンドから直接データを取得していました。

https://zenn.dev/masa5714/articles/40883d972ab2c7

上記記事の方法を使って Postman 経由でリクエストを投げたところ、データベースの内容が丸見えの状態になっていました。本来であれば自分が投稿したデータのみ見えるべきですが、他のユーザーが入力したプロンプトまで丸見えになっていました。また、プロフィールも見えてしまっており、非公開のメールアドレスすらも見えておりました。

リクエストは自由にwhereやlimitなどの条件も書き加えることができるので、まるで管理者のような感覚でデータにアクセスできてしまっていました。(スクレイピングがしやすい状態)

僕の理性が働きデータを隅々までは見ておりません。そのためプロンプトに個人を特定する重要なデータが含まれているかは未確認ですが、一種のデータ流出事件とも言えると思います。

幸いなことにこのサービスでは住所などのクリティカルなデータは(恐らく)含まれていなかったので大きな問題はありませんでしたが、一歩間違えれば全国ニュース級の大問題に発展していたことでしょう。

本件について詳しい内容は下記記事に書いておりますのでご覧ください。
https://zenn.dev/masa5714/articles/811e75dba7a34b

この事件を受けて対策を考える( .rpcを使おう )

僕としては .select() の利用は極力控え、 .rpc() 経由で自作関数を動かして SELECT文を実行するという形にすべきだろうと落ち着きました。

RLSのポリシーで SELECTオペレーションをひとまず FALSE にしておき、必要に応じて少しずつ条件を緩めていく運用が最適でしょう。なお、RLSを有効にするとデフォルトでFALSEの状態となりますが明示的にFALSEとしておいた方が事故の可能性は抑えられると思います。( id = auth.uid() など、自身のデータだけにアクセスできる条件にしておく程度。また、DELETEをするにはそのデータにアクセスできる状態である必要があったりします。)

RLSポリシーでFALSEにしてしまうとデータ取得ができなくなるので自作関数を作ります。

※下記はあくまで書き方の例です。

PostreSQLの自作関数の例
CREATE OR REPLACE FUNCTION public.get_posts()
  RETURNS TABLE ( -- 任意の値だけを返すようにできる
    post_id uuid,
    post_title text,
    post_contents text,
    post_datetime timestamp with time zone
  )
  LANGUAGE plpgsql
  SECURITY DEFINER -- この記述によってポリシーがFALSEでもrpc経由なら取得できるようになる
  SET search_path TO 'public'
AS $function$
BEGIN
  RETURN QUERY -- 下記のSELECTの結果がASでつけた名前で返却される
    SELECT
      posts.id AS post_id,
      posts.title AS post_title,
      posts.contents AS post_contents,
      posts.updated_at AS post_datetime
    FROM public.posts
    ORDER BY updated_at DESC
    LIMIT 10;
END
$funciton$;

SECURITY DEFINER を記述すると関数作成時のユーザーで実行できるようになります。
このようにして、 .rpc("get_posts") で関数を呼び出すことで自由なリクエストをさせない、管理者だけが制御できるSELECT文を作れるようになりました。

さいごに

Supabaseの気軽にSELECTできる素晴らしさを潰してしまうような解決策ですが現状はこれ以外の方法が見当たりません。しかし、この方法を使わないとデータ流出の温床になったり、スクレイピングし放題だったりとガバガバな状態になってしまいます。

リスクを取るよりはマシだと思って妥協する他ありません。

Discussion