FixVibe

// docs / baas security / supabase service role exposure

Supabase service-role-nyckel exponerad i JavaScript: vad det betyder och hur du hittar den

Supabase service-role-nyckel är huvudnyckeln till din databas. Den som har den kringgår row-level security, kan läsa varje kolumn av varje tabell och kan skriva eller radera vad som helst. Den är designad att leva enbart i serverkod — aldrig i webbläsaren. När ett AI-kodningsverktyg levererar den till JavaScript-bundlen är din databas i praktiken publik. Den här artikeln förklarar JWT-formen som identifierar en läckt nyckel, de tre AI-verktygsmönstren som producerar läckan, vad du gör under den första timmen efter detektion och hur du skannar efter den automatiskt innan användarna gör det.

Vad service-role-nyckeln är

Supabase utfärdar två distinkta nycklar per projekt: anon-nyckeln (även kallad publicerbar nyckel i nyare projekt) och service_role-nyckeln. Båda är JSON Web Tokens signerade med ditt projekts JWT-secret. Skillnaden är role-claimen som är inbakad i JWT-payloaden — anon för den publika nyckeln, service_role för huvudnyckeln. PostgREST, Supabase Storage och Supabase Auth växlar alla till kringgå-allt-läge när de ser service_role-claimen.

Avkoda en valfri Supabase-nyckel på jwt.io och titta på payloaden. Formen på en service-role-JWT är omisskännlig:

Avkodad payload för en service-role-JWT (visas som ett syntaxmarkerat block nedan).

json
{
  "iss": "supabase",
  "ref": "[project-ref]",
  "role": "service_role",
  "iat": 1700000000,
  "exp": 2000000000
}

Nyare Supabase-projekt utfärdar secret-liknande nycklar med prefixet sb_secret_ istället för en JWT. Beteendet är identiskt — allt som bär sb_secret_ i en publik bundle är lika katastrofalt.

Hur AI-kodningsverktyg läcker service-role-nyckeln

Vi har sett samma tre mönster över tusentals vibe-kodade appar. Vart och ett börjar med att en utvecklare ber ett AI-verktyg om hjälp och slutar med att service-nyckeln hamnar inlinad i en bundle.

Mönster 1: En enda .env-fil med NEXT_PUBLIC_-prefix

Utvecklaren ber AI-verktyget att "sätta upp Supabase" och accepterar en enda .env med båda nycklarna. AI-verktyget — tränat på ett korpus där de flesta miljövariabler exponeras via NEXT_PUBLIC_* — prefixar båda med NEXT_PUBLIC_. Next.js inlinear allt som matchar det prefixet i klientbundlen vid byggtillfället. Deploya till Vercel, och service-nyckeln ligger i main.[hash].js.

Mönster 2: Fel nyckel i createClient-anrop

Utvecklaren klistrar in båda nycklarna i en config.ts-fil som AI:n genererade, och AI:n fyller av misstag det webbläsarseende createClient()-anropet med process.env.SUPABASE_SERVICE_ROLE_KEY. Bygget drar in variabeln, och JWT:n landar i bundlen.

Mönster 3: Service-role-nyckel hårdkodad i seed-skript

Utvecklaren ber AI-verktyget att skriva ett skript som seedar databasen. AI:n hårdkodar service-role-nyckeln direkt i filen (istället för att läsa från miljön), commitar filen till repot, och det publika GitHub-repot eller den deployade appens /scripts/seed.js-rutt serverar nu nyckeln.

Hur FixVibes bundle-scan upptäcker läckan

FixVibes bundle-secrets-check laddar ner varje JavaScript-fil som den deployade appen refererar — entry-chunks, lazy-laddade chunks, web workers, service workers — och kör dem genom en detektor som avkodar allt som matchar JWT-formen (eyJ[base64-header].eyJ[base64-payload].[signature]). Om den avkodade payloaden innehåller "role": "service_role", rapporterar skanningen det som ett kritiskt fynd med filsökväg och exakt rad där nyckeln visas. Samma check matchar också det nyare sb_secret_*-mönstret via prefix.

Skanningen autentiserar sig aldrig med den upptäckta nyckeln. Den identifierar formen och rapporterar läckan — att använda nyckeln för att bevisa exploaterbarhet skulle vara obehörig åtkomst till din databas. Beviset finns i JWT-payloaden själv.

Upptäckt — vad du gör under den första timmen

En läckt service-role-nyckel är en runtime-nödsituation. Anta att nyckeln har skrapats — angripare övervakar publika bundles i realtid. Behandla databasen som komprometterad tills du har roterat nyckeln och granskat den senaste aktiviteten.

  1. Rotera nyckeln omedelbart. I Supabase-dashboarden, gå till Project Settings → API → Service role key → Reset. Den gamla nyckeln invalideras inom sekunder. Varje serverkod som använder nyckeln måste uppdateras och deployas på nytt innan rotationen träder i kraft.
  2. Granska den senaste databasaktiviteten. Öppna Database → Logs i dashboarden. Filtrera på de senaste 7 dagarna. Leta efter ovanliga SELECT *-queries mot tabeller med PII, stora UPDATE- eller DELETE-satser och förfrågningar från IP-adresser utanför din kända infrastruktur. Supabase loggar x-real-ip-headern på varje förfrågan.
  3. Kontrollera storage-objekt. Besök Storage → Logs och granska de senaste filnedladdningarna. En läckt service-role-nyckel ger även kringgå-allt-åtkomst till privata buckets.
  4. Ta bort nyckeln från versionshantering. Även efter rotation innebär JWT:n i din git-historik att den är upptäckbar i det publika repot. Använd git filter-repo eller BFG Repo-Cleaner för att skrubba den ur historiken, sedan force-push (varna medarbetare först).
  5. Skanna om efter fix. Kör en ny FixVibe-scanning mot den nydeployade appen. Bundle-secrets-fyndet ska försvinna. Bekräfta att ingen service_role-JWT och ingen sb_secret_*-sträng återstår i någon chunk.

Förebygga läckan från början

Den strukturella lösningen är namndisciplin plus skyddsräcken på verktygsnivå:

  • Prefixera aldrig service-nyckeln med NEXT_PUBLIC_*, VITE_* eller något annat bundle-inlining-prefix. Namnkonventionen är gränsen — varje ramverk respekterar den.
  • Håll service-nyckeln helt utanför .env på utvecklarmaskinen. Läs den från en hemlighetshanterare (Doppler, Infisical, Vercels krypterade env-variabler) vid deploy, commita den aldrig lokalt.
  • <strong>Mark every Supabase client construction with explicit context.</strong> Files named <code>supabase/browser.ts</code> use the anon key; files named <code>supabase/server.ts</code> use the service-role key with <code>import 'server-only'</code> at the top. The <code>server-only</code> import causes a build error if a client component tries to consume the module.
  • <strong>Add a pre-commit hook that greps for JWT-shaped strings.</strong> <code>git diff --staged | grep -E 'eyJ[A-Za-z0-9_-]+\.eyJ[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+'</code> catches both anon and service tokens before they leave your machine.
  • Lägg till en CI-gate som skannar bygg-outputen. Efter next build, greppa .next/static/chunks/-outputen efter strängen service_role. Låt bygget misslyckas om något matchar.
bash
# Pre-commit hook: refuse any staged JWT-shaped string.
git diff --staged \
  | grep -E 'eyJ[A-Za-z0-9_-]+\.eyJ[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+' \
  && echo "JWT detected in staged changes — refusing commit" \
  && exit 1

# CI gate: fail the build if "service_role" shipped to the static bundle.
grep -RE 'service_role|sb_secret_' .next/static/chunks/ \
  && echo "Service-role credential leaked into bundle" \
  && exit 1

Vanliga frågor

Hur snabbt hittar angripare faktiskt läckta Supabase service-role-nycklar?

Publika bundle-scanners genomsöker nya deployments inom minuter. Forskare har dokumenterat fungerande exploits mot nya Supabase-projekt på under en timme från första deploy. Behandla varje service-role-exponering som ett 60-minutersfönster, inte ett 60-dagarsfönster.

Räcker det att rotera nyckeln, eller måste jag anta dataexfiltration?

Rotationen invaliderar den läckta nyckeln men ogör inte data som redan dragits ut. Om dina tabeller innehåller PII, betalningsdata eller annan reglerad data kan du ha anmälningsplikt enligt GDPR (72 timmar), CCPA eller HIPAA. Granska loggarna och konsultera juridisk rådgivning om granskningen visar misstänkt åtkomst.

Kan RLS skydda mig om service-role-nyckeln läcker?

Nej. Row-level security kringgås helt av service_role-claimen. Det är by design — nyckeln finns just för att låta backend-kod hoppa över RLS för adminoperationer. Begränsningen är att säkerställa att nyckeln aldrig når en kontext där en angripare kan läsa den.

Gäller detta den nya Supabase publicerbar-/secret-nyckel-modellen (<code>sb_publishable_</code> / <code>sb_secret_</code>)?

Ja — identisk riskklass. sb_secret_*-nyckeln är det nya secret-nyckel-formatet som ersätter service-role-JWT:n för nyare projekt. Allt som bär sb_secret_* i en bundle är lika katastrofalt som en läckt service-role-JWT. FixVibes bundle-secrets-detektor matchar båda formerna.

Vad gäller anon-/publicerbar-nyckeln — är den säker i bundlen?

Ja, by design. Anon-nyckeln är avsedd att leva i webbläsaren och är det varje Supabase-webbklient använder. Dess säkerhet beror helt på att RLS är korrekt konfigurerat på varje publik tabell. Se artikeln Supabase RLS-scanner för vad som ska kontrolleras.

Nästa steg

Kör en FixVibe-scanning mot din produktions-URL — bundle-secrets-checken är gratis, ingen registrering, och rapporterar service_role-exponering på under en minut. Kombinera detta med artikeln Supabase RLS-scanner för att verifiera att RLS-lagret gör sitt jobb, och Checklista för Supabase storage-bucket-säkerhet för att låsa filåtkomst. För bakgrund till varför AI-verktyg genererar den här läckklassen så pålitligt, läs Varför AI-kodningsverktyg lämnar säkerhetsluckor.

// skanna din baas-yta

Hitta den öppna tabellen innan någon annan gör det.

Klistra in en produktions-URL. FixVibe identifierar BaaS-leverantörerna din app pratar med, fingeravtryckar deras publika endpoints och rapporterar vad en oautentiserad klient kan läsa eller skriva. Gratis, ingen installation, inget kort.

  • Gratisnivå — 3 skanningar/månad, inget kort vid registrering.
  • Passiv BaaS-fingeravtryckning — ingen domänverifiering krävs.
  • Supabase, Firebase, Clerk, Auth0, Appwrite och fler.
  • AI-fixprompter på varje fynd — klistra tillbaka in i Cursor / Claude Code.
Kör en gratis BaaS-skanning

ingen registrering krävs

Supabase service-role-nyckel exponerad i JavaScript: vad det betyder och hur du hittar den — Docs · FixVibe