SupabaseHigh

RLS Bypass: Unauthorized UPDATE

Tables allow unauthenticated or cross-user updates due to missing or overly permissive UPDATE policies.

Last updated 2026-01-15

What Is This Vulnerability

An RLS bypass on UPDATE occurs when a table's update policy fails to properly restrict which rows a user can modify. UPDATE policies require both a USING clause (which rows can be targeted) and a WITH CHECK clause (what values are allowed in the updated row). When either clause is set to true or omits an auth.uid() check, any user can modify any row in the table.

-- VULNERABLE: Any user can update any row
CREATE POLICY "Allow updates"
  ON user_profiles FOR UPDATE
  USING (true)
  WITH CHECK (true);

A particularly dangerous variant is when USING is correctly scoped but WITH CHECK is not, allowing a user to change the user_id column to take ownership of another user's data.

Why It's Dangerous

Unauthorized UPDATE access is one of the most damaging vulnerabilities because it allows an attacker to:

  • Modify other users' data: Change profile information, email addresses, or passwords
  • Escalate privileges: Update their own role from user to admin in a roles table
  • Manipulate financial records: Alter order amounts, credit balances, or payment statuses
  • Deface content: Modify published posts, product listings, or public-facing data
  • Bypass business logic: Change order statuses, subscription tiers, or feature flags

Unlike INSERT, UPDATE attacks modify existing legitimate data, making them harder to detect and more destructive. The original data may be permanently lost if not backed up.

How to Detect

Inspect UPDATE policies in the system catalog:

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

Check that both qual (USING) and with_check reference auth.uid() with a proper ownership check. AuditYourApp tests this by authenticating as one user and attempting to update a row belonging to a different user. If the update succeeds, the policy is vulnerable.

How to Fix

Create UPDATE policies that verify both row ownership and that the updated row still belongs to the same user:

DROP POLICY "Allow updates" ON user_profiles;

CREATE POLICY "Users can update own profile"
  ON user_profiles FOR UPDATE
  USING (auth.uid() = id)
  WITH CHECK (auth.uid() = id);

The USING clause ensures users can only target their own rows. The WITH CHECK clause ensures they cannot change the id or user_id to hijack another user's row. Both clauses are critical.

For tables with non-editable columns, consider using a restrictive policy combined with column-level grants:

-- Prevent users from updating sensitive columns
REVOKE UPDATE (role, is_admin, credit_balance) ON user_profiles FROM authenticated;

CREATE POLICY "Users can update own non-sensitive fields"
  ON user_profiles FOR UPDATE
  USING (auth.uid() = id)
  WITH CHECK (auth.uid() = id);

This defense-in-depth approach ensures that even if the policy is slightly misconfigured, critical columns remain protected at the database level.

Scan your app for this vulnerability

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

Run Free Scan