GUIDE 05 · WEB APPLICATIONS AND APIs
Supabase security: check RLS and access to data
Supabase row-level security must enforce the permissions of actual application users, not just succeed in an administrative SQL session. Test two independent users and known rows, including permitted reads, forbidden cross-owner access and attempts to change ownership. Keep privileged keys on trusted servers.
Check your Supabase applicationSign-in, ownership verification and consent are required. Authorized scenarios, monitoring and CI require Pro or Business.
On this page
Understand browser-to-data access
A browser can call the data API directly when the application is configured that way. The access boundary must therefore hold at the database and API layers. Hiding a UI action does not protect a row. Confirm which role and authenticated identity reach the database for each request.
Separate public and privileged keys
A publishable or anon key is intended for public-client use under appropriate grants and RLS. A secret or service_role key belongs on a trusted server and may bypass RLS. Do not use a privileged context as evidence that a user-level policy works; test the actual authenticated client identity.
| Key | Intended location | Boundary |
|---|---|---|
| publishable / anon | Public client with correct RLS | Not a permission to bypass policy |
| secret / service_role | Trusted server only | May bypass RLS; never in a browser bundle |
| User JWT | Authenticated user request | Verify token and permissions; do not publish |
Primary sources: Primary reference 1 · Primary reference 2
Describe the rules of your table
For each action, specify the row owner or organization, caller role and permitted fields. Enable RLS and review grants. A read policy does not automatically authorize a safe write. Shared data requires its own explicit policy rather than assumptions based on the owner-only example.
A minimal owner-read policy
The example below restricts SELECT to the authenticated owner. Replace its table and column names with your own schema, and keep other actions separate. The downloadable local fixture uses PostgreSQL with a clearly named test identity function; it is not a claim that an actual hosted Supabase project was exercised.
alter table public.documents enable row level security;
create policy "owner reads" on public.documents
for select to authenticated
using (owner_id = (select auth.uid()));Primary sources: Primary reference 1 · Primary reference 2
Continue: Exposed .env, .git or API keys: contain the leak and verify the fix
Check your Supabase applicationCheck inserts and ownership changes
WITH CHECK validates the proposed new row, while USING selects the rows an operation may act on. Verify that A cannot insert a row owned by B or transfer its own row to B without permission. Also verify a permitted edit so an unusable table is not mistaken for a secure application.
Use a two-user matrix
Check owner reads, cross-owner reads, anonymous access, foreign-owner inserts and ownership changes. A denied SELECT may return an empty array rather than a 403. The local SQL fixture runs seven assertions as an ordinary non-login role and rolls back its objects; cloud validation remains a separate acceptance step.
| Local SQL scenario | Expected outcome |
|---|---|
| A reads A | One visible row |
| A reads B | No visible rows |
| B reads B | One visible row |
| Anonymous read | No private rows |
| A inserts owner B | WITH CHECK denies |
| A changes owner to B | WITH CHECK denies |
| After the local test | Transaction rolled back |
Primary sources: Primary reference 1 · Primary reference 2
Continue: Next.js security: check APIs, Server Actions and secrets
Inspect adjacent access paths
Review storage buckets, views, RPC functions and backend services that use privileged keys. Their behavior is not proven by a policy on one table. Evaluate each exposed path under the identities that really use it. Scantelio’s configured HTTP scenarios complement, rather than replace, a database policy review.
Apply the change and repeat
Version the schema and policy change, deploy it to a test environment and repeat both denials and positive controls. Then follow your normal reviewed release process. Preserve the tested schema, identities, sanitized outcomes and deployment revision; never include working user tokens in the evidence.
Reproducible examples and downloads
All example data is synthetic. Downloads contain no active keys or customer records. Run examples only in your own isolated test environment.
Example verification record: versions, actions and observations

Questions and answers
Is the anon key a secret?
It is designed for public-client use with correct permissions. Privileged secret/service-role keys are different.
Does enabling RLS solve every access issue?
No. Policies, grants, functions, storage and privileged server paths still need review.
Why does a denied read return 200?
Row filtering may legitimately produce a successful response with an empty result set.
Can I test through SQL Editor alone?
That does not reproduce the actual user context. Use authenticated client requests as well.
Was the example tested in hosted Supabase?
The supplied automated example was tested in local PostgreSQL. The protocol states that limitation explicitly.
Can Scantelio infer the intended policy?
No. Provide identities, known objects and expected permissions for supported HTTP access checks.
Editorial team and sources
Scantelio editorial team: methodology and limitations. Example verified: 2026-11-02.
Apply the check to your own project
Check your Supabase applicationChoose your project after signing in. No scan runs without verification and consent.
