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
usertoadminin 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 ScanRelated
vulnerabilities
RLS Bypass: Unauthorized INSERT
Tables allow unauthenticated or cross-user inserts due to missing or overly permissive INSERT policies.
vulnerabilities
RLS Bypass: Unauthorized DELETE
Tables allow unauthenticated or cross-user deletes due to missing or overly permissive DELETE policies.
vulnerabilities
Authenticated Cross-User Write Access
Authenticated users can modify or delete other users' data due to write policies lacking ownership checks.