What Is This Vulnerability
Public table read access occurs when a table's RLS SELECT policy allows the anon (anonymous) role to read data. While RLS is enabled, the policy explicitly or implicitly grants read access to unauthenticated users. This can be intentional for public content (e.g., published blog posts), but is often a misconfiguration that exposes data meant only for authenticated users.
-- RLS is enabled, but the SELECT policy allows anonymous reads
CREATE POLICY "Public read"
ON products FOR SELECT
USING (true); -- Both anon and authenticated roles can read
The distinction between this and "Missing RLS" is that RLS is enabled here, but the policies are configured to allow public access. The distinction from "RLS Bypass: Unauthorized SELECT" is that this specifically concerns anonymous (unauthenticated) access.
Why It's Dangerous
Even if the data seems non-sensitive, public read access can expose:
- User-generated content that should require an account to view (gating content behind auth)
- Metadata that reveals your application's structure, user count, or business metrics
- Internal identifiers such as UUIDs, foreign keys, or references to other tables that aid further attacks
- Scraped data that competitors or bad actors can harvest at scale
- Pricing, inventory, or configuration data that gives competitors an advantage
Attackers commonly enumerate public tables first to understand the database schema, then pivot to more targeted attacks against tables with write vulnerabilities.
How to Detect
Test anonymous access by making API calls without an Authorization header (or with just the anon key):
curl 'https://YOUR_PROJECT.supabase.co/rest/v1/TABLE_NAME?select=*&limit=5' \
-H "apikey: YOUR_ANON_KEY"
If data is returned, the table is publicly readable. You can also query RLS policies to find those that do not check for authentication:
SELECT tablename, policyname, roles, qual
FROM pg_policies
WHERE schemaname = 'public'
AND cmd = 'SELECT'
AND roles @> ARRAY['anon'];
AuditYourApp scans every table as an anonymous user and flags any that return data.
How to Fix
If the table should only be accessible to authenticated users, update the policy:
DROP POLICY "Public read" ON products;
-- Only authenticated users can read
CREATE POLICY "Authenticated users can read products"
ON products FOR SELECT
TO authenticated
USING (true);
The TO authenticated clause restricts the policy to the authenticated role, excluding anonymous users. If only certain rows should be public, add a condition:
-- Only published products are publicly readable
CREATE POLICY "Public can view published products"
ON products FOR SELECT
USING (status = 'published' AND visibility = 'public');
-- Authenticated users can also see their own drafts
CREATE POLICY "Users can view own products"
ON products FOR SELECT
TO authenticated
USING (auth.uid() = owner_id);
Review each table and determine the minimum access level required. Default to authenticated-only access and only open tables to anonymous users when there is a clear business requirement.
Scan your app for this vulnerability
AuditYourApp automatically detects security misconfigurations in Supabase and Firebase projects. Get actionable remediation in minutes.
Run Free ScanRelated
vulnerabilities
Missing Row Level Security Policy
Tables without RLS are fully exposed to any user with the anon key, allowing unrestricted read and write access to all rows.
vulnerabilities
Public Table Write Access
Tables are writable by anonymous users, allowing unauthenticated visitors to insert, update, or delete data.
vulnerabilities
Authenticated User Data Leak
Authenticated users can read other users' data due to SELECT policies that do not enforce row-level ownership checks.