SupabaseHigh

RLS Bypass: Unauthorized SELECT

Overly permissive SELECT policies allow users to read data they should not have access to, exposing sensitive information.

Last updated 2026-01-15

What Is This Vulnerability

Even when Row Level Security is enabled on a table, the policies themselves may be misconfigured. An RLS bypass on SELECT occurs when the policy's USING clause is too broad, effectively granting read access to rows the user should not see. Common mistakes include using USING (true) which permits all reads, omitting the auth.uid() check, or writing conditions that always evaluate to true for the anonymous role.

This vulnerability is subtler than missing RLS entirely because the developer has taken the step of enabling RLS, giving a false sense of security. However, the policy logic itself fails to restrict access properly.

-- VULNERABLE: This policy allows ANY user to read ALL rows
CREATE POLICY "Allow read access"
  ON orders FOR SELECT
  USING (true);

Why It's Dangerous

An attacker with the anonymous key (or any authenticated user) can read every row in the table. Depending on the table contents this may expose:

  • Personal identifiable information (PII) such as names, emails, and phone numbers
  • Financial data including order amounts, billing details, and transaction history
  • Internal application state such as admin flags, permission levels, and feature flags
  • Other users' private content, messages, or documents

The data exfiltration can be performed silently using standard Supabase client calls, making it difficult to detect without query logging and anomaly detection.

How to Detect

Review your RLS policies for overly permissive conditions:

SELECT schemaname, tablename, policyname, permissive, cmd, qual
FROM pg_policies
WHERE schemaname = 'public'
  AND cmd = 'SELECT';

Look for policies where the qual column contains true or lacks a reference to auth.uid(). AuditYourApp tests this by attempting to read data as an unauthenticated user and as an authenticated user without ownership of the rows. If data is returned that should be restricted, the policy is overly permissive.

How to Fix

Replace permissive SELECT policies with conditions that verify ownership or role:

-- Drop the overly permissive policy
DROP POLICY "Allow read access" ON orders;

-- Create a properly scoped policy
CREATE POLICY "Users can view own orders"
  ON orders FOR SELECT
  USING (auth.uid() = user_id);

-- For shared data, use team-based or role-based conditions
CREATE POLICY "Team members can view team orders"
  ON orders FOR SELECT
  USING (
    team_id IN (
      SELECT team_id FROM team_members
      WHERE user_id = auth.uid()
    )
  );

If you intentionally need some rows to be publicly readable (e.g., published blog posts), scope the policy to only those rows:

CREATE POLICY "Public can view published posts"
  ON posts FOR SELECT
  USING (status = 'published');

Always test policies by querying as different roles (anon, authenticated, service_role) to verify the expected behavior.

Scan your app for this vulnerability

AuditYourApp automatically detects security misconfigurations in Supabase and Firebase projects. Get actionable remediation in minutes.

Run Free Scan