ASAASA Standard
Active Phase 1Production Foundation

service_role Key Exposure

Auth Safety · AUTH-01 · ADM-17 · Priority: P0

Why It Matters

The Supabase service_role key bypasses all Row Level Security policies. If this key appears in client-side code, any user can extract it from the browser and gain full read/write access to your entire database — including other users' data, billing records, and admin configurations.

This is the single most dangerous secret exposure in the Supabase ecosystem. Unlike the anon key (which is meant to be public), the service_role key is a server-only credential.

Priority: P0 — Immediate remediation required. This is equivalent to publishing your database admin password.

Affected Stack: Supabase, Next.js, any framework using Supabase client


The Problem

AI code generators frequently use the service_role key in client-side Supabase client initialization — because it "just works" (it bypasses RLS, so all queries succeed during development). The key ends up in files that are bundled and shipped to the browser.

Common locations where AI tools place the service_role key:

  • src/lib/supabase.ts (shared client used by both server and client components)
  • app/layout.tsx or app/providers.tsx
  • Environment variables prefixed with NEXT_PUBLIC_
  • Inline in React components
// ❌ What AI tools typically generate
import { createClient } from '@supabase/supabase-js';

export const supabase = createClient(
  process.env.NEXT_PUBLIC_SUPABASE_URL!,
  process.env.NEXT_PUBLIC_SUPABASE_SERVICE_ROLE_KEY! // Exposed to browser!
);

Why This Happens

AI tools optimize for "it works" — and the service_role key makes everything work because it bypasses RLS. The AI doesn't distinguish between server-only and client-safe credentials.


The Fix

Use the anon key for client-side code and the service_role key only in server-side code (API routes, server actions, server components).

// ✅ Client-side: anon key only
import { createBrowserClient } from '@supabase/ssr';

export const supabase = createBrowserClient(
  process.env.NEXT_PUBLIC_SUPABASE_URL!,
  process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY! // Safe for browser
);
// ✅ Server-side: service_role key (never exposed to browser)
import { createClient } from '@supabase/supabase-js';

export const supabaseAdmin = createClient(
  process.env.SUPABASE_URL!,        // No NEXT_PUBLIC_ prefix
  process.env.SUPABASE_SERVICE_ROLE_KEY!  // No NEXT_PUBLIC_ prefix
);

Key rules:

  • Never prefix service_role key with NEXT_PUBLIC_
  • Never import the admin client in client components
  • Use separate client instances for browser vs server

References


ADM-17: Admin Context

The same vulnerability applies in admin contexts — admin dashboards and admin API routes must never use the service_role key in client-side code. Admin operations should use server actions or API routes that keep the service_role key on the server.


Related Checks


Is your app safe? Run Free Scan →