🦁

ログインなしで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ベースのキー(anonservice_role)しか使えない環境が複数存在する。

以下の環境では、現状JWTベースのキーのみが利用可能:

  1. ローカル開発環境(Supabase CLI)

  2. Self-hosting環境

  3. Edge Functions

    • Edge FunctionsはJWT検証のみをサポートしており、anonservice_role のJWTベースのキーを使用する
    • Publishable/Secret keysを使う場合は --no-verify-jwt オプションが必要
    • その場合、Supabaseプラットフォームは apikey ヘッダーを検証しないため、Edge Function内で独自に認証ロジックを実装する必要がある

7. まとめ

Supabaseで匿名アクセスを安全に許可するためのポイントをまとめる。

重要なポイント

  1. Publishable key(または anon JWT)は公開してOK

    • クライアントサイドのコードに含めても問題ない
    • ただし、RLSが絶対に必要
  2. セキュリティの本質はRLSポリシーにある

    • APIキーは基本的なDoS攻撃の防止程度の役割
    • 実際のアクセス制御はRLSポリシーで行われる
    • すべてのテーブルでRLSを有効化し、適切なポリシーを設定する
  3. 新しいPublishable/Secret keysを使う(可能であれば)

    • JWTベースのキーは非推奨
    • 段階的な移行が可能で、セキュリティ運用がしやすい
    • ただし、ローカル開発、Self-hosting、Edge Functions(JWT検証使用時)ではJWTベースのキーが必要
  4. JWTベースのキーの制約を理解する

    • JWT secretと密結合しており、個別に無効化できない
    • 特にモバイルアプリでは、ユーザーが古いバージョンを使い続ける可能性がある
    • キーのローテーション時は強制アップデートの仕組みが必要
  5. service_role キーは絶対にクライアントサイドで使わない

    • すべてのRLSをバイパスする
    • バックエンドやEdge Functionsでのみ使用
    • 漏洩した場合はすぐに削除する

Discussion