Supabase and Firebase Credentials Exposed in Your Repo - VibeDoctor 
← All Articles 🔑 Secret Detection Critical

Supabase and Firebase Credentials Exposed in Your Repo

Vibe coding tools often commit Supabase anon keys and Firebase configs to Git. Learn which credentials are safe to expose and which are not.

GIT-009

Quick Answer

Supabase anon keys and Firebase client configs are designed to be public - they are used in browser-side code. The danger is the Supabase service_role key and Firebase Admin SDK credentials, which bypass all security rules. AI tools like Bolt and Lovable frequently commit the wrong key to Git or expose it in client-side code, giving anyone who finds it full database access.

Supabase Has Two Keys - Only One Is Safe to Expose

Supabase projects come with two JWT-based API keys accessible from your project dashboard. Most developers, and almost all AI-generated code, conflate them:

The anon key (anonymous key) is the public-facing credential. It is designed to be included in client-side code, committed to public repositories, and bundled into your JavaScript. It respects every Row Level Security (RLS) policy you define on your tables. If RLS is configured correctly, the anon key can only access data that unauthenticated users are supposed to see.

The service_role key is entirely different. It is a superuser credential. It bypasses all RLS policies entirely and has full read/write access to your entire database. It is intended only for server-side administrative tasks - like a backend function that needs to create users, run migrations, or access data across all accounts. It should never appear in client-side code, never be committed to Git, and never be prefixed with NEXT_PUBLIC_.

GitGuardian's research shows that Supabase service keys are among the most commonly exposed credentials in vibe-coded projects, because AI tools generating full-stack apps often reach for the most powerful credential available to make things work quickly.

How AI Tools Get This Wrong

When you prompt Bolt or Lovable to "connect to my Supabase database," it scaffolds a Supabase client. If you paste both keys from your dashboard into the prompt context, the AI will often use the service_role key - because it works for everything, while the anon key requires RLS to be set up first.

// ❌ BAD - service_role key in client-side Next.js code
// lib/supabase.ts (loaded in the browser)

import { createClient } from '@supabase/supabase-js';

const supabase = createClient(
  'https://xyzxyzxyz.supabase.co',
  // This is the service_role key - it bypasses ALL Row Level Security.
  // Anyone who opens DevTools Network tab can see this key.
  'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6Inh5enh5enh5eiIsInJvbGUiOiJzZXJ2aWNlX3JvbGUiLCJpYXQiOjE2MjkxOTI4MDAsImV4cCI6MTk0NDc2ODgwMH0.EXAMPLE'
);

The decoded JWT payload of a service_role key contains "role": "service_role". Anyone can base64-decode the middle segment of any JWT and see exactly what role it grants. Your key's role is not a secret - which means if you expose the service_role key, you've handed over administrative database access.

// ✅ GOOD - Correct key separation in Next.js
// lib/supabase-client.ts (browser-safe, uses anon key)
import { createBrowserClient } from '@supabase/ssr';

export function createClient() {
  return createBrowserClient(
    process.env.NEXT_PUBLIC_SUPABASE_URL!,
    process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY! // anon key - safe to expose
  );
}

// lib/supabase-server.ts (server-only, uses service_role key)
import { createClient as createAdminClient } from '@supabase/supabase-js';

export function createAdminSupabase() {
  return createAdminClient(
    process.env.NEXT_PUBLIC_SUPABASE_URL!,
    process.env.SUPABASE_SERVICE_ROLE_KEY! // NOT NEXT_PUBLIC_ - server only
  );
}

Firebase: The Config Object vs. Admin SDK

Firebase has an analogous split. The Firebase client config object - the one with apiKey, authDomain, projectId - is designed to be public. Despite the name, the apiKey in this config is not a secret; it identifies your Firebase project to Google's servers. Firebase Security Rules govern what authenticated and unauthenticated users can access.

The Firebase Admin SDK credentials are a different matter entirely. The service account JSON file downloaded from the Google Cloud Console contains a private RSA key. This credential bypasses all Firestore Security Rules and Firebase Auth checks. According to the CSA's 2024 Cloud Security Report, misconfigured cloud service accounts are the leading cause of cloud data breaches, with exposed service account keys cited in 34% of incidents.

Credential Safe in Client Code? Safe to Commit to Git? Bypasses Security Rules?
Supabase anon key Yes (with RLS) Yes (with RLS) No
Supabase service_role key Never Never Yes - full bypass
Firebase client config Yes (with Security Rules) Yes (with Security Rules) No
Firebase Admin SDK JSON Never Never Yes - full bypass
Firebase Database Secret (legacy) Never Never Yes

Row Level Security: The Anon Key's Only Defense

The Supabase anon key being safe to expose is conditional on RLS being enabled and correctly configured. This is a critical caveat. Vibe-coded apps frequently have RLS disabled - either because the AI scaffolded the project that way, because the developer turned it off to make queries work, or because they never enabled it in the first place.

If RLS is disabled on a Supabase table, the anon key effectively acts like a service_role key for that table. Anyone with your anon key (which is public) can read and write all rows. Veracode's 2024 report found that missing authorization controls affect 72% of applications at some point - and RLS misconfiguration is the Supabase-specific manifestation of this problem.

Before treating your anon key as safe, verify that every table with sensitive data has RLS enabled and that your policies are tested:

// ✅ GOOD - Verify RLS is enabled via Supabase SQL editor
// Run this query to check which tables have RLS disabled:

// SELECT schemaname, tablename, rowsecurity
// FROM pg_tables
// WHERE schemaname = 'public'
// ORDER BY tablename;

// Any row where rowsecurity = false on a table with user data
// means your anon key can read ALL rows in that table.

How to Find Exposed Credentials in Your Repo

Tools like VibeDoctor (vibedoctor.io) automatically scan your codebase for exposed Supabase service_role keys and Firebase Admin SDK credentials, flagging specific file paths and line numbers. Free to sign up.

Manually, look for these patterns: any JWT starting with eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9 that decodes to a service_role payload, any .json file containing "private_key_id" (the telltale field in Firebase service account files), and any environment variable named NEXT_PUBLIC_SUPABASE_SERVICE_ROLE_KEY - a mistake where both parts of the naming convention are wrong simultaneously.

FAQ

My Supabase anon key is already on GitHub - do I need to rotate it?

If RLS is enabled and correctly configured, a public anon key is expected and not a vulnerability. However, if you have any tables with RLS disabled, or if you are unsure of your RLS configuration, treat it as compromised. You can rotate the anon key from the Supabase dashboard under Settings > API.

What does the Supabase service_role key actually give an attacker?

Full read and write access to every table in your database, bypassing all Row Level Security policies. An attacker can read all user data, modify records, delete data, and - depending on your Supabase configuration - use it to create admin-level auth tokens. Treat it with the same sensitivity as a database root password.

Is it safe to commit the Firebase firebaseConfig object?

Yes, Firebase's client config (with apiKey, authDomain, etc.) is designed to be public. Google's documentation explicitly states these values are not secrets. Your Firebase Security Rules are what protect your data. However, the Firebase Admin SDK service account JSON file is a private key and must never be committed.

How do I use the service_role key safely in a Next.js app?

Store it as a server-only environment variable (no NEXT_PUBLIC_ prefix) and only instantiate the admin Supabase client inside server-side code: API Routes, Server Actions, or getServerSideProps. Never import or use the admin client in any file that is included in the client bundle.

Can Bolt or Lovable apps have RLS enabled?

Yes. Supabase allows you to enable RLS from the Table Editor and write policies in the SQL editor. The challenge with AI-generated apps is that the AI rarely writes the policies - it enables the feature but leaves tables wide open, which is functionally equivalent to having RLS disabled. Always test your policies with an authenticated user and an anonymous user before deploying.

Scan your codebase for this issue - free

VibeDoctor checks for GIT-009 and 128 other issues across 15 diagnostic areas.

SCAN MY APP →
← Back to all articles View all 129+ checks →