ログインなしでSupabaseのデータを利用したい。権限はどうする?
1. はじめに
サービス開発をしていると「ログインしていないユーザーにもデータを公開したい」というケースに遭遇する。
例えば:
- ブログ記事の一覧を誰でも閲覧できるようにしたい
- ECサイトで商品一覧は誰でも閲覧できるようにしたい
このような場合、「権限はどうやって管理すればいいの?」という疑問が生まれる。
この記事では、Supabaseでこのようなケースにどう対応するかを紹介する
2. TL;DR(結論)
✅ Publishable key(または anon)を利用
- ソースコードに含めても、ブラウザから見えても問題なし
- これらのキーは基本的なDoS攻撃の防止程度の役割
⚠️ ただし、Row Level Security(RLS)の設定が絶対に必須
- RLSなしでキーを公開すると、全データが丸見えになる
- セキュリティの本質はキーの管理ではなく、RLSポリシーの設計にある
3. Supabase APIキーの種類と役割
APIキーとSupabase Authの違い
まず理解すべき重要な点として、APIキーとSupabase Authは異なる役割を持つ:
| 役割 | 質問 | 答え |
|---|---|---|
| APIキー | 何がプロジェクトにアクセスしているか? | Webページ、モバイルアプリ、サーバー、Edge Functionなど |
| Supabase Auth | 誰がプロジェクトにアクセスしているか? | 山田太郎さん、佐藤花子さん、田中一郎さんなど |
つまり:
- APIキー: アプリケーションコンポーネント(何)を認証する
- Supabase Auth: 個々のユーザー(誰)を認証する
APIキーはユーザーを区別せず、アプリケーション間のみを区別する。
4種類のAPIキー
Supabaseプロジェクトには、4種類のAPIキーが存在する。それぞれの役割と使い分けを理解することが重要。
| キー種類 | 形式 | 権限 | RLS | 使用方法 |
|---|---|---|---|---|
|
Publishable key (公開キー) |
sb_publishable_... |
Low | ✅ 有効 | ✅ オンラインで公開しても安全: Webページ、モバイル・デスクトップアプリ、GitHub Actions、CLI、ソースコード ✅ 推奨 |
|
Secret keys (秘密キー) |
sb_secret_... |
Elevated | ❌ バイパス | ❌ バックエンドのみで使用: サーバー、すでに保護されたAPI(管理パネル)、Edge Functions、マイクロサービスなど。プロジェクトのデータに完全にアクセスし、Row Level Securityをバイパスする ✅ 推奨 |
|
anon (JWT) |
JWT (長期間有効) |
Low | ✅ 有効 | Publishable keyとまったく同じ ⚠️ 非推奨(まだ使用可能) |
|
service_role (JWT) |
JWT (長期間有効) |
Elevated | ❌ バイパス | Secret keysとまったく同じ ⚠️ 非推奨(まだ使用可能) |
JWTベースのキーが非推奨な理由
- 全てがJWT secretと密結合していて、個別の変更ができない。
- 特にモバイルアプリでは、アプリが公開されるまでのラグや、ユーザーが古いバージョンのアプリを使い続ける可能性などがあるため、JWT形式のキーをinvalidにすると問題が発生する。一方、Publishable/Secret keysは複数のキーを同時に有効化できるため、段階的な移行が可能
など
4. 匿名アクセスでの権限管理の仕組み
Supabaseでは、APIキーだけでなく、PostgreSQLの ロール(role)とRow Level Security(RLS) を組み合わせて権限を管理する。
PostgreSQLロールの自動切り替え
Publishable key(または anon JWT)を使用してアクセスする場合:
-
ログインしていない場合:
anonロールが適用される -
ログイン済みの場合:
authenticatedロールが適用される
このロールの切り替えは、Supabaseが自動的に行う。
Publishable key と anon が提供する保護
これらのキーは、プロジェクトのデータ、パフォーマンス、請求に対する第一層の保護を提供する:
- 最小限の知識を要求することで、基本的なDoS攻撃を防ぐ
- ボット、スクレイパー、自動脆弱性スキャナー、その他のランダムなインターネット活動を無視することで、請求を保護する
これらのキーでは防げないもの(公開コンポーネントからキーを取得することは常に可能なため):
- 静的・動的コード解析やリバースエンジニアリング
- ブラウザのネットワークインスペクター
- CSRF、XSS、フィッシング攻撃
- 中間者攻撃(MITM)
Row Level Security(RLS)
テーブルの各行に対するアクセス制御を行うPostgreSQLの機能。
すべてのテーブルでRLSを有効化する。RLSを有効化していないテーブルは、誰でもアクセス可能になる。
また、RLSを有効化しただけでは、すべてのアクセスが拒否される。適切なポリシーを作成する必要がある。
service_role と Secret keys の特別な権限
Secret keys や service_role JWT を使用すると:
-
すべてのRLSポリシーをバイパスする(
BYPASSRLS属性) - すべてのテーブルのすべての行にアクセス可能
- 絶対にクライアントサイドで使用してはいけない。バックエンド処理で必要な場合にのみ使用する。
5. 実装例
ここでは、ブログ記事を管理する簡単な例を示す。
テーブルの作成
CREATE TABLE articles (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
title TEXT NOT NULL,
content TEXT NOT NULL,
author_id UUID REFERENCES auth.users(id),
created_at TIMESTAMPTZ DEFAULT now()
);
-- RLSを有効化
ALTER TABLE articles ENABLE ROW LEVEL SECURITY;
RLSポリシーの設定
-- 閲覧は全員可能(匿名ユーザーも認証済みユーザーも)
CREATE POLICY "記事は誰でも閲覧可能"
ON articles
FOR SELECT
TO anon, authenticated
USING (true);
-- 編集は自分の記事のみ可能
CREATE POLICY "自分の記事のみ編集可能"
ON articles
FOR UPDATE
TO authenticated
USING (author_id = auth.uid())
WITH CHECK (author_id = auth.uid());
クライアントサイドのコード(JavaScript)
import { createClient } from '@supabase/supabase-js'
// Publishable keyを使用(ソースコードに含めてOK)
const supabase = createClient(
'https://your-project.supabase.co',
'your-publishable-key' // sb_publishable_xxx
)
// 記事一覧を取得(ログイン不要、誰でも閲覧可能)
const { data, error } = await supabase
.from('articles')
.select('*')
.order('created_at', { ascending: false })
// 記事を更新(ログインが必要、ログインしていたら自動でauthenticated roleになる、自分の記事のみ)
const { data: updated, error: updateError } = await supabase
.from('articles')
.update({ title: '更新されたタイトル', content: '更新された内容' })
.eq('id', articleId)
6. JWTベースのキーが必要なケース
Publishable/Secret keysが推奨されているが、実際にはJWTベースのキー(anon や service_role)しか使えない環境が複数存在する。
以下の環境では、現状JWTベースのキーのみが利用可能:
-
ローカル開発環境(Supabase CLI)
-
Self-hosting環境
-
Edge Functions
- Edge FunctionsはJWT検証のみをサポートしており、
anonやservice_roleのJWTベースのキーを使用する - Publishable/Secret keysを使う場合は
--no-verify-jwtオプションが必要 - その場合、Supabaseプラットフォームは
apikeyヘッダーを検証しないため、Edge Function内で独自に認証ロジックを実装する必要がある
- Edge FunctionsはJWT検証のみをサポートしており、
7. まとめ
Supabaseで匿名アクセスを安全に許可するためのポイントをまとめる。
重要なポイント
-
Publishable key(または
anonJWT)は公開してOK- クライアントサイドのコードに含めても問題ない
- ただし、RLSが絶対に必要
-
セキュリティの本質はRLSポリシーにある
- APIキーは基本的なDoS攻撃の防止程度の役割
- 実際のアクセス制御はRLSポリシーで行われる
- すべてのテーブルでRLSを有効化し、適切なポリシーを設定する
-
新しいPublishable/Secret keysを使う(可能であれば)
- JWTベースのキーは非推奨
- 段階的な移行が可能で、セキュリティ運用がしやすい
- ただし、ローカル開発、Self-hosting、Edge Functions(JWT検証使用時)ではJWTベースのキーが必要
-
JWTベースのキーの制約を理解する
- JWT secretと密結合しており、個別に無効化できない
- 特にモバイルアプリでは、ユーザーが古いバージョンを使い続ける可能性がある
- キーのローテーション時は強制アップデートの仕組みが必要
-
service_role キーは絶対にクライアントサイドで使わない
- すべてのRLSをバイパスする
- バックエンドやEdge Functionsでのみ使用
- 漏洩した場合はすぐに削除する
Discussion