Auth Bypass on Supabase
How Auth Bypass Manifests in Supabase
Auth bypass in Supabase typically occurs when developers rely on client-side row-level security (RLS) policies without properly enforcing them on the server side. Supabase's architecture creates several unique vulnerability patterns:
Client-Side Policy Reliance
// Vulnerable: client trusts RLS policies
const { data, error } = await supabase
.from('users')
.select('*')
.eq('email', userEmail)
The above code assumes RLS policies will prevent unauthorized access, but if policies are misconfigured or missing, anyone can query any user's data. This is particularly dangerous because Supabase's client libraries don't enforce authentication at the library level.
Missing Row-Level Security
-- Vulnerable: no RLS policies defined
CREATE TABLE profiles (
id UUID DEFAULT gen_random_uuid(),
user_id UUID REFERENCES auth.users(id),
email TEXT,
PRIMARY KEY (id)
);
Without explicit RLS policies, Supabase tables default to public access. Even with auth enabled, unauthenticated requests can read/write data if policies aren't defined.
Service Role Key Exposure
// Vulnerable: service_role key in client
const supabase = createClient(
process.env.SUPABASE_URL,
process.env.SUPABASE_SERVICE_ROLE_KEY // NEVER expose this client-side
);
Service role keys bypass all RLS policies and can access any data. Embedding these keys in client applications creates immediate auth bypass vulnerabilities.
Broken JWT Validation
// Vulnerable: trusting unverified JWT claims
const { data } = await supabase
.from('orders')
.select('*')
.eq('user_id', decodedToken.user_id) // No verification
Attackers can forge JWT tokens with arbitrary claims if the application doesn't properly validate token signatures or expiration.
Cross-Tenant Data Leakage
-- Vulnerable: policies don't scope by tenant
CREATE POLICY "Users can view their own profiles"
ON profiles
FOR SELECT
USING (true); -- Should be: USING (auth.uid() = user_id)
Multi-tenant applications often fail to scope policies by tenant ID, allowing users to access other tenants' data through simple ID manipulation.
Supabase-Specific Detection
Detecting auth bypass in Supabase requires both static analysis and runtime scanning. Here's how to identify these vulnerabilities:
Static Analysis Patterns
# Check for service_role key usage
grep -r "service_role" . --include="*.js" --include="*.ts" --include="*.json"
# Find missing RLS policies
supabase sql "SELECT schemaname, tablename FROM pg_tables WHERE schemaname = 'public' AND tablename NOT IN (
SELECT tablename FROM pg_policies
)"
Runtime Testing with middleBrick
middleBrick's black-box scanning specifically tests Supabase auth bypass patterns:
# Scan Supabase endpoint
middlebrick scan https://your-project.supabase.co/api/v1/endpoint
# Scan with OpenAPI spec for deeper analysis
middlebrick scan --spec openapi.json https://your-project.supabase.co/api/v1/endpoint
The scanner tests for:
- Missing or permissive RLS policies
- Service role key exposure
- JWT token manipulation vulnerabilities
- Cross-tenant data access
- Authentication bypass through parameter manipulation
Database-Level Detection
-- Find tables without RLS policies
SELECT
schemaname,
tablename,
(SELECT count(*) FROM pg_policies WHERE schemaname = pol.schemaname AND tablename = pol.tablename) as policy_count
FROM pg_tables pol
WHERE schemaname = 'public'
ORDER BY policy_count;
Network Traffic Analysis
# Test for auth bypass by removing auth headers
curl -H "Content-Type: application/json" \
-H "apikey: $(SUPABASE_ANON_KEY)" \
-X GET https://your-project.supabase.co/rest/v1/users
If this returns data without proper authentication, RLS policies are missing or misconfigured.
Supabase-Specific Remediation
Fixing auth bypass in Supabase requires a defense-in-depth approach. Here are specific remediation strategies:
Proper RLS Policy Implementation
-- Secure: properly scoped policies
CREATE POLICY "Users can view their own profiles"
ON profiles
FOR SELECT
USING (auth.uid() = user_id);
CREATE POLICY "Users can update their own profiles"
ON profiles
FOR UPDATE
USING (auth.uid() = user_id);
CREATE POLICY "Users can delete their own profiles"
ON profiles
FOR DELETE
USING (auth.uid() = user_id);
-- Multi-tenant example
CREATE POLICY "Users can view their own tenant data"
ON orders
FOR SELECT
USING (auth.uid() = user_id AND tenant_id = current_setting('app.current_tenant_id'));
Server-Side Validation
// Secure: server-side auth validation
import { createClient } from '@supabase/supabase-js'
import jwt from 'jsonwebtoken'
const supabaseAdmin = createClient(
process.env.SUPABASE_URL,
process.env.SUPABASE_SERVICE_ROLE_KEY // Server-only
)
export async function getUserProfile(userId, token) {
try {
const decoded = jwt.verify(token, process.env.JWT_SECRET)
if (decoded.uid !== userId) {
throw new Error('Unauthorized')
}
const { data, error } = await supabaseAdmin
.from('profiles')
.select('*')
.eq('user_id', userId)
.single()
return data
} catch (error) {
throw new Error('Authentication failed')
}
}
API Gateway Protection
// Next.js API route with proper auth
import { NextApiRequest, NextApiResponse } from 'next'
import { createClient } from '@supabase/supabase-js'
export default async function handler(
req: NextApiRequest,
res: NextApiResponse
) {
const supabase = createClient(
process.env.SUPABASE_URL,
process.env.SUPABASE_ANON_KEY
)
if (!req.headers.authorization) {
return res.status(401).json({ error: 'Missing token' })
}
const token = req.headers.authorization.replace('Bearer ', '')
const { data: { user } } = await supabase.auth.getUser(token)
if (!user) {
return res.status(401).json({ error: 'Invalid token' })
}
// Continue with authorized request
}
Service Role Key Management
Continuous Monitoring with middleBrick
# Add to CI/CD pipeline
middlebrick scan --spec openapi.json \
--fail-below B \
--output json > security-report.json
# GitHub Action integration
- name: middleBrick API Security Scan
uses: middlebrick/middlebrick-action@v1
with:
api-url: ${{ secrets.SUPABASE_URL }}
fail-threshold: B
Testing Remediation Effectiveness
# Test that auth bypass is fixed
curl -X GET \
https://your-project.supabase.co/rest/v1/profiles \
-H "apikey: $(SUPABASE_ANON_KEY)" \
-H "Content-Type: application/json" \
| jq '.error' # Should return auth errorRelated CWEs: authentication
| CWE ID | Name | Severity |
|---|---|---|
| CWE-287 | Improper Authentication | CRITICAL |
| CWE-306 | Missing Authentication for Critical Function | CRITICAL |
| CWE-307 | Brute Force | HIGH |
| CWE-308 | Single-Factor Authentication | MEDIUM |
| CWE-309 | Use of Password System for Primary Authentication | MEDIUM |
| CWE-347 | Improper Verification of Cryptographic Signature | HIGH |
| CWE-384 | Session Fixation | HIGH |
| CWE-521 | Weak Password Requirements | MEDIUM |
| CWE-613 | Insufficient Session Expiration | MEDIUM |
| CWE-640 | Weak Password Recovery | HIGH |