Open3

Supabase Authのベストプラクティスを求めて

shibainushibainu

背景

個人開発で、Supabase Authによる認証機能の導入を行っているのですが、以下のXのポストを中心に、VibeコーディングとIDaaSの相性の悪さがX上で活発に議論されており、私自身ももっとSupabase Authの仕様を理解する必要があると感じました。

https://x.com/labelmake/status/1913155551963259148

認証機能を自作すると堅牢な設計・実装を行なったつもりでも、意図しないかたちでセキュリティホールが生まれやすいなどのリスクもあるため、認証機能をSupabase AuthやFirebase AuthenticationなどのIDaaSに任せたい一方で、IDaaSを使用する際にも適切なセキュリティ設定を行わないと、ユーザー情報が外部に丸見えになってしまうなど、セキュリティ上のリスクが存在します。

そこで、Supabase Authのベストプラクティスを求めてということで、以下を中心に調べたことを記録として残していきたいと思います。

  • Supabase Authを使用した安全な認証フローの検討
  • クライアントサイドのSupabaseクライアントからSupabase DBに直アクセスすることは危険なのか?
  • Row Level SecurityとPolicy設定
  • Publishable, Secret key (旧anon, service_role key)

本スクラップを通して、Supabase Authのより良いプラクティスがエンジニアコミュニティの中で広まっていけば良いなと考えているので、
 
「そこは間違っている!」
「ここの内容怪しくない?」
 
などありましたら、お気軽にコメントいただけると嬉しいです!!

shibainushibainu

Supabaseの思想と最大の注意点

Supabaseの特徴の一つに、Row Level Security (RLS)がある。

SupabaseはデータベースにPostgreSQLを採用しており、その機能であるRLSおよびPolicyを開発者が適切に設定することで、セキュリティを担保している。

そして、RLSが存在するからこそ、「クライアントサイドのSupabaseクライアントから、publishable keyを使用して、DBに直アクセスしても安全である」というのがSupabaseのセキュリティ部分の根底にある思想であり、大きな特徴だとと感じた。

実際、↑で安全なのは間違いない。開発者がPublicスキーマ内に存在するテーブルに対して、RLSを有効にした上で、適切なPolicy設定を行うことで、クライアントからDBに直アクセスしても安全である。
(ただ、PIIを含むテーブルをPublicスキーマに置くことはセキュアなのか?それについては次回)

しかし、SupabaseのダッシュボードにあるTable Editor経由で作成したテーブルにのみRLSがデフォルトで有効になる一方で、それ以外の方法で作成したテーブルに対しては、開発者がRLSを手動で有効にする必要があるというが最大の注意点である。RLSの設定を忘れると、攻撃者がクライアントサイドのpublishable keyを不正に取得することで、RLSがオフになっているテーブル内のすべてのデータにアクセスできてしまう。そのテーブルに、PII(個人情報)が含まれていると考えるかなり怖い。

↓にあるように、悪意のあるChrome拡張機能などから、リアルタイムにpublishable keyを取得できてしまうし、publishable keyはそもそも外部に露出することを前提としているものである。
https://github.com/hand-dot/supabase-rls-checker

そのため、公式ドキュメントでも言及されているように、Publicスキーマ内に存在する全てのテーブルに対して、RLSを有効にすることを徹底したい。

RLS must always be enabled on any tables stored in an exposed schema. By default, this is the public schema.

ただ、RLSやPolicyの設定漏れなどのヒューマンエラーが怖いので、次回以降でセキュアなシステム設計を検討してみたい。

shibainushibainu

Supabaseを使用したセキュアなシステム設計の検討①

ありがちだけどPIIを扱うには危険な構成

以下の構成を考えてみる。ブラウザなどのクライアントサイドから、Publishable Keyを使って、DBに直アクセスする構成である。

SupabaseのデータベースはPostgreSQLであるため、デフォルトでPublicスキーマが用意されている。PostgreSQLで、明示的にスキーマ名を指定しなかった場合、テーブルのデフォルトの格納先はPublicスキーマになるので、Supabase DBにおいてもそうなることが予想される。

そしてSupabaseのデフォルト設定では、DataAPI経由でPublicスキーマにアクセス可能になっている。

このDataAPIというのが普段開発者からは見えない部分というか、抽象化されている部分なので、曖昧になりがちだが、DataAPIはPostgreSQLデータベースから自動生成されたPostgRESTというAPIサーバのことである。Supabase公式ドキュメントのアーキテクチャの説明を見ると分かりやすい。PostgRESTの存在により、開発者にはフロントエンドからDBに直アクセスしているように見えるが、実はそうではない。

そして、これらの構成を活用すると高速にプロダクト開発できるという利点もあるが、クライアントでPublishable keyを保持していることで、セキュリティ上の落とし穴も多いように思う。

Securing your dataでは、↓のように全テーブルにRLSをオンにして、適切なPolicy設定を行うことで、データが安全に管理されると記述されている。

You can keep your data secure while accessing the Data APIs from the frontend, so long as you:

  • Turn on Row Level Security (RLS) for your tables
  • Use your Supabase anon key when you create a Supabase client

しかし、前回も言及したように、RLSやPolicyの設定ミスがあれば、攻撃者がPublishable Keyを取得することで、データベースからデータを盗めてしまう。

さらに、Hardening the Data APIでは、以下のように言及されている。

Private schemas
We highly recommend creating a private schema for storing tables that you do not want to expose via the Data API.

Disabling the Data API
You can disable the Data API entirely if you never intend to use the Supabase client libraries or the REST and GraphQL data endpoints. For example, if you only access your database via a direct connection on the server, disabling the Data API gives you the greatest layer of protection.

つまり、「外部に公開したくないデータを保管するためにPrivateスキーマを作成することを強く推薦している。Edge Functions経由でデータベースにアクセスして、DataAPIを無効にすることで、最大級のプロテクション層ができる」と言っている。

PIIを扱うテーブルは、Privateスキーマ内に配置して、DataAPI経由でアクセスできないようにすることがベストではないか。この辺りをしっかり調べないと、デフォルトのPublicスキーマにPIIを置いてしまった、そしてRLSとPolicyの設定漏れがあったなんてことも、Vibeコーディング時代には全然あり得ると思うので注意したい。