Valtik Studios
Supabase · RLS · RPC · Next.js

Supabase Security Review

The pentest your Supabase-backed SaaS needs. Row Level Security policy audit, SECURITY DEFINER RPC review, GraphQL surface mapping, Storage + Realtime + Edge Function testing, Next.js App Router CSRF and IDOR probing. Delivered by a working pentester who breaks Supabase apps for a living.

Supabase is powerful and quiet about what it expects of you.

Supabase gives you a Postgres database, an auth server, realtime, storage, edge functions, and a GraphQL API in one weekend of setup. That is the whole pitch. The same stack that moves you fast is also seven separate attack surfaces stitched together with RLS policies you wrote yourself, and nobody tells you when you got one wrong.

The typical Supabase-backed SaaS ships with RLS that looks right to a developer reading the code, but fails in production against one of a handful of well-known patterns: a SECURITY DEFINER RPC that trusts a caller-supplied tenant ID, a Realtime channel that broadcasts rows the subscriber has no right to see, a Storage bucket whose policy allows cross-tenant listing, a GraphQL Mutation surface that the developer forgot was exposed at all.

On top of that you probably have a Next.js App Router app serving the frontend, and Next.js does not enable CSRF protection on its /api/* route handlers by default. A DELETE /api/me endpoint that trusts the session cookie alone is one stored XSS or one subdomain compromise away from a cross-tenant account wipe.

Supabase + Next.js + Stripe + Inngest + Clerk is a common stack. Every integration point between those services is a place the security model has to survive. Most reviews we do find at least one finding at one of those seams.

What a Supabase security review covers

Row Level Security policy audit

Every policy on every table in the public schema gets read, traced, and probed. The questions we answer:

  • Does SELECT policy match INSERT/UPDATE/DELETE policy intent, or is there a gap?
  • Does the policy use auth.uid() or does it trust a column the caller can write?
  • Is the policy tied to a JOIN that silently returns zero rows when the join fails, masking a bypass?
  • Do the policies protect against cross-tenant writes via UPDATE with a fabricated tenant_id?
  • Are there tables without RLS enabled at all? (The default.)
  • Are there policies with using (true) that were meant to be temporary?

SECURITY DEFINER function review

This is where most Supabase apps fail. A SECURITY DEFINER function runs as its owner, typically postgres, which bypasses RLS. If the function takes a caller-controlled tenant ID as a parameter and uses it to pick the row it operates on, the entire authorization model is bypassed. We enumerate every SECURITY DEFINER function, read its body, and probe for this pattern.

A real example from a recent engagement: a function called increment_leads_used(p_account_id uuid, p_count int)let any authenticated user inflate any other tenant's usage counter to INT_MAX or reset it to zero. RLS on the accounts table was correctly configured. The function simply did not scope to auth.uid(). The fix was trivial (derive scope from auth.uid(), reject negative counts). The flaw had shipped months before anyone looked at it.

GraphQL Mutation surface enumeration

pg_graphql auto-exposes your schema at /graphql/v1. TheinsertInto*, update*, and deleteFrom* builtins are RLS-protected, but custom SECURITY DEFINER functions show up in the Mutation type and bypass RLS just like the REST RPC path does. We introspect the full Mutation surface, map every function, flag the suspicious ones, and (with permission) probe them actively.

Storage bucket policy audit

Supabase Storage runs on Postgres RLS. Every bucket has its own policy and every object can carry metadata. We test:

  • Public buckets that should not be public
  • Policies that allow cross-tenant listing
  • Path-traversal in object keys (../ in user-supplied filenames)
  • Pre-signed URL leakage and replay
  • MIME-type trust issues when you serve user-uploaded content back to users

Realtime channel authorization

Realtime is a WebSocket-backed pubsub that enforces RLS on subscriptions. Usually. We test the edge cases:

  • Subscribing to postgres_changes on a table and receiving rows that belong to other tenants
  • Broadcast channels with no authorization (anyone can subscribe tochat:room:123)
  • Presence state that leaks user identity across tenants
  • JWT expiration during a long-lived subscription (does the connection drop or continue serving stale perms)

Edge Function review

Deno-based Edge Functions are code we read. We look for webhook signature verification that was skipped, internal SSRF in fetch() with user-controlled URLs, secret leakage through error paths and stack traces, JWT validation that trusts the sub claim without checking the signature, and third-party library choices that introduce supply-chain risk.

Next.js App Router integration testing

Most Supabase apps have a Next.js frontend that mediates requests through/api/* route handlers. The common flaws:

  • DELETE /api/me-style endpoints with no CSRF defense or re-auth requirement
  • Mass-assignment on PATCH routes that let callers update columns they should not
  • Session cookie trust without Origin/Referer validation
  • Server Actions that expose privileged operations based on client-side state
  • Middleware that enforces auth on the HTML route but not the API route that serves the same data
  • Stack traces and debug JSON leaked in error responses

Third-party integration testing

When Stripe, Inngest, Resend, Clerk, or other providers touch your stack, each integration point is a trust boundary. We test Stripe webhook forgery, Inngest signing key enforcement, customer-portal session creation without active subscription, pre-auth billing-portal enumeration, and JWT template mismatches between Clerk and Supabase.

What you get

  • An executive summary a non-technical founder can forward to a buyer or investor.
  • A technical findings report with exact reproduction steps, HTTP requests, and SQL diffs for every finding.
  • Severity ratings mapped to CVSS 4.0 and to real-world exploit difficulty.
  • Concrete remediation — the SQL policy, the RPC rewrite, the Next.js middleware, the Stripe check. Not "improve your security posture."
  • A retest included in the fixed-fee scope. We re-run the same attack paths after you ship the fix and either close the finding or note why the fix was insufficient.
  • A "what you do well" section. Every review we run turns up things the team got right that most teams get wrong. That goes in the report too, because fair attribution makes the hard findings easier to accept.

Who this is for

  • SaaS founders on Supabase with paying customers who are asking for a pentest.
  • Supabase apps pursuing SOC 2 — the auditor does not accept "we use Supabase" as a security control.
  • Apps with sensitive data — PHI, financial records, customer PII, any multi-tenant situation where cross-tenant leakage is a recall-level event.
  • Teams that just had a security scare. A suspicious log entry, a customer report, a bug bounty tip. We do post-incident Supabase reviews.
  • Engineering teams who want external verification that their RLS model actually works against an authenticated attacker.

Methodology

Every engagement follows Valtik's six-phase methodology: scoping, passive reconnaissance, authenticated active testing, exploit verification, reporting, and retest. What makes a Supabase review different is the depth at the data-tier: we spend more time reading RLS policies and SECURITY DEFINER function bodies than we spend on HTTP endpoints.

Testing is always from the posture of a standard authenticated user of your product. We do not ask for the service_role key because we do not want to simulate a compromised admin. The threat model is a paying customer who decides to attack other customers.

Read more about our general approach on the methodology page.

Pricing

TierScopePrice range
FocusedRLS + RPC review only. Single Supabase project.$3,500 – $6,500
StandardAbove, plus GraphQL, Storage, Realtime, and Next.js /api layer.$6,500 – $15,000
Full StackAbove, plus Edge Functions, Stripe / Inngest / Resend integrations, and Clerk / NextAuth auth flow review.$12,000 – $25,000

Final pricing is set after a scoping call. Number of tables, presence of tool-calling agents or AI features, vertical (healthcare and financial services carry more regulatory overlay), and whether the engagement includes a written report you will forward to a third party (auditor, buyer, insurance) all shift the number.

Related open-source tooling

We publish the tooling we developed during Supabase engagements as open source:

  • supabase-rpc-auth-scanner — audits a project's GraphQL Mutation surface for SECURITY DEFINER RPCs that accept caller-controlled tenant IDs. Pip-installable.
  • nextjs-csrf-scanner — probes Next.js App Router /api/* route handlers for CSRF on state-changing verbs.

Both are MIT-licensed and have no hidden paid tier. Running them yourself before scoping a full engagement is encouraged.

Ready to scope a Supabase review?

Reach out and describe your stack. We respond within 24 business hours with scoping questions and a proposed engagement window. We also run a free automated external security check that can help narrow where the paid engagement should focus.

Ready to start?

Free website security check. No obligation, no sales pitch. Delivered as a plain-English findings report in 48 hours.

Request Free Check