// docs / baas security / supabase service role exposure
Klucz roli serwisowej Supabase ujawniony w JavaScript: co to oznacza i jak go znaleźć
Klucz roli serwisowej Supabase to klucz główny do Twojej bazy danych. Każdy, kto go posiada, omija zabezpieczenia na poziomie wierszy, może odczytać każdą kolumnę każdej tabeli i może zapisywać lub usuwać cokolwiek zechce. Jest zaprojektowany, aby istnieć wyłącznie w kodzie po stronie serwera — nigdy w przeglądarce. Kiedy narzędzie kodowania AI wysyła go do pakietu JavaScript, Twoja baza danych jest w efekcie publiczna. Ten artykuł wyjaśnia kształt JWT identyfikujący wyciekły klucz, trzy wzorce narzędzi AI produkujące wyciek, co zrobić w pierwszej godzinie po wykryciu i jak automatycznie to skanować, zanim zrobią to użytkownicy.
Czym jest klucz roli serwisowej
Supabase wydaje dwa różne klucze dla każdego projektu: klucz anon (zwany również kluczem publicznym w nowszych projektach) i klucz service_role. Oba są tokenami JSON Web Token podpisanymi sekretem JWT projektu. Różnica polega na zastrzeżeniu role wbudowanym w ładunek JWT — anon dla klucza publicznego, service_role dla klucza głównego. PostgREST, Supabase Storage i Supabase Auth wszystkie przełączają się w tryb omijania wszystkiego, gdy widzą zastrzeżenie service_role.
Zdekoduj dowolny klucz Supabase na jwt.io i spójrz na ładunek. Kształt JWT roli serwisowej jest niezawodny:
Zdekodowany ładunek JWT roli serwisowej (pokazany jako blok z podświetleniem składni poniżej).
{
"iss": "supabase",
"ref": "[project-ref]",
"role": "service_role",
"iat": 1700000000,
"exp": 2000000000
}Nowsze projekty Supabase wydają klucze w stylu sekretu z prefiksem sb_secret_ zamiast JWT. Zachowanie jest identyczne — wszystko, co przenosi sb_secret_ w publicznym pakiecie, jest równie katastrofalne.
Jak narzędzia kodowania AI wyciekają klucz roli serwisowej
Widzieliśmy te same trzy wzorce w tysiącach aplikacji kodowanych w stylu vibe. Każdy zaczyna się od tego, że programista prosi narzędzie AI o pomoc, a kończy umieszczeniem klucza serwisowego inline w pakiecie.
Wzorzec 1: Pojedynczy plik .env z prefiksem NEXT_PUBLIC_
Programista prosi narzędzie AI o „skonfigurowanie Supabase" i akceptuje pojedynczy .env z oboma kluczami. Narzędzie AI — wytrenowane na korpusie, w którym większość zmiennych środowiskowych jest ujawniana przez NEXT_PUBLIC_* — dodaje prefiks NEXT_PUBLIC_ do obu. Next.js wstawia inline wszystko pasujące do tego prefiksu do pakietu klienta podczas budowania. Wyślij do Vercel, a klucz serwisowy znajdzie się w main.[hash].js.
Wzorzec 2: Niewłaściwy klucz w wywołaniu createClient
Programista wkleja oba klucze do pliku config.ts, który wygenerował AI, a AI przez pomyłkę wypełnia wywołanie createClient() po stronie przeglądarki za pomocą process.env.SUPABASE_SERVICE_ROLE_KEY. Kompilacja pobiera zmienną, a JWT trafia do pakietu.
Wzorzec 3: Klucz roli serwisowej zakodowany na sztywno w skryptach seed
Programista prosi narzędzie AI o napisanie skryptu wypełniającego bazę danych. AI koduje klucz roli serwisowej na sztywno bezpośrednio w pliku (zamiast odczytywać ze środowiska), zatwierdza plik do repozytorium, a publiczne repozytorium GitHub lub trasa /scripts/seed.js wdrożonej aplikacji teraz obsługuje klucz.
Jak skanowanie pakietu FixVibe wykrywa wyciek
Kontrola sekretów pakietu FixVibe pobiera każdy plik JavaScript, do którego odwołuje się wdrożona aplikacja — fragmenty wejściowe, fragmenty ładowane leniwie, web workery, service workery — i przepuszcza je przez detektor, który dekoduje wszystko pasujące do kształtu JWT (eyJ[base64-header].eyJ[base64-payload].[signature]). Jeśli zdekodowany ładunek zawiera "role": "service_role", skanowanie zgłasza to jako krytyczne znalezisko z ścieżką do pliku i dokładną linią, w której pojawia się klucz. Ta sama kontrola pasuje również do nowszego wzorca sb_secret_* przez prefiks.
Skanowanie nigdy nie uwierzytelnia się odkrytym kluczem. Identyfikuje kształt i zgłasza wyciek — użycie klucza do udowodnienia możliwości wykorzystania byłoby nieautoryzowanym dostępem do Twojej bazy danych. Dowód znajduje się w samym ładunku JWT.
Wykryto — co zrobić w pierwszej godzinie
Wyciekły klucz roli serwisowej to nagła sytuacja środowiska wykonawczego. Załóż, że klucz został pobrany — atakujący monitorują publiczne pakiety w czasie rzeczywistym. Traktuj bazę danych jako naruszoną, dopóki nie obrócisz klucza i nie skontrolujesz ostatniej aktywności.
- Natychmiast obróć klucz. W panelu Supabase przejdź do Project Settings → API → Service role key → Reset. Stary klucz jest unieważniany w ciągu sekund. Każdy kod po stronie serwera używający klucza musi zostać zaktualizowany i wdrożony ponownie, zanim rotacja zostanie zastosowana.
- Sprawdź ostatnią aktywność bazy danych. Otwórz Database → Logs w panelu. Filtruj według ostatnich 7 dni. Szukaj nietypowych zapytań
SELECT *wobec tabel z PII, dużych instrukcjiUPDATElubDELETEi żądań z adresów IP spoza znanej infrastruktury. Supabase loguje nagłówekx-real-ipprzy każdym żądaniu. - Sprawdź obiekty magazynu. Odwiedź Storage → Logs i przejrzyj ostatnie pobrania plików. Wyciekły klucz roli serwisowej daje dostęp z omijaniem wszystkiego również do prywatnych koszów.
- Usuń klucz z kontroli wersji. Nawet po rotacji pozostawienie JWT w historii gita oznacza, że jest do odkrycia w publicznym repozytorium. Użyj
git filter-repolub BFG Repo-Cleaner, aby usunąć go z historii, a następnie wykonaj force-push (najpierw ostrzeż współpracowników). - Ponowne skanowanie po naprawie. Uruchom świeże skanowanie FixVibe wobec ponownie wdrożonej aplikacji. Znalezisko sekretów pakietu powinno zniknąć. Potwierdź, że żaden fragment nie zawiera już JWT
service_roleani ciągusb_secret_*.
Zapobieganie wyciekowi w pierwszej kolejności
Strukturalna naprawa to dyscyplina nazewnictwa plus zabezpieczenia na poziomie narzędzi:
- Nigdy nie dodawaj prefiksu klucza serwisowego z
NEXT_PUBLIC_*,VITE_*ani żadnego innego prefiksu wstawiania do pakietu. Konwencja nazewnictwa to granica — każdy framework ją respektuje. - Trzymaj klucz serwisowy całkowicie poza
.envna maszynie programisty. Odczytaj go z menedżera sekretów (Doppler, Infisical, zaszyfrowane zmienne env Vercel) przy wdrożeniu, nigdy nie zatwierdzaj go lokalnie. - <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.
- Dodaj bramkę CI, która skanuje wyjście kompilacji. Po
next buildwykonaj grep wyjścia.next/static/chunks/dla ciąguservice_role. Niech kompilacja się nie uda, jeśli cokolwiek pasuje.
# 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 1Często zadawane pytania
Jak szybko atakujący faktycznie znajdują wyciekłe klucze roli serwisowej Supabase?
Skanery pakietów publicznych przeczesują nowe wdrożenia w ciągu minut. Badacze udokumentowali działające exploity wobec nowych projektów Supabase w mniej niż godzinę od pierwszego wdrożenia. Traktuj każde ujawnienie roli serwisowej jako 60-minutowe okno, a nie 60-dniowe.
Czy rotacja klucza wystarczy, czy muszę założyć eksfiltrację danych?
Rotacja unieważnia wyciekły klucz, ale nie cofa danych już pobranych. Jeśli Twoje tabele zawierają PII, dane płatnicze lub jakiekolwiek regulowane dane, możesz mieć obowiązek powiadomienia zgodnie z GDPR (72 godziny), CCPA lub HIPAA. Sprawdź logi i skonsultuj się z radcą prawnym, jeśli audyt pokazuje podejrzany dostęp.
Czy RLS może mnie chronić, jeśli klucz roli serwisowej wycieknie?
Nie. Zabezpieczenia na poziomie wierszy są całkowicie omijane przez zastrzeżenie service_role. To z założenia — klucz istnieje właśnie po to, aby kod backendowy mógł pomijać RLS dla operacji administracyjnych. Środkiem zaradczym jest upewnienie się, że klucz nigdy nie dociera do kontekstu, w którym atakujący może go odczytać.
Czy to dotyczy nowego modelu klucza publicznego / sekretnego Supabase (<code>sb_publishable_</code> / <code>sb_secret_</code>)?
Tak — identyczna klasa ryzyka. Klucz sb_secret_* to nowy format klucza sekretnego, który zastępuje JWT roli serwisowej dla nowszych projektów. Wszystko, co przenosi sb_secret_* w pakiecie, jest równie katastrofalne jak wyciekły JWT roli serwisowej. Detektor sekretów pakietu FixVibe pasuje do obu kształtów.
A co z kluczem anon / publicznym — czy jest bezpieczny w pakiecie?
Tak, z założenia. Klucz anon jest przeznaczony do życia w przeglądarce i jest tym, czego używa każdy klient web Supabase. Jego bezpieczeństwo zależy całkowicie od tego, czy RLS jest poprawnie skonfigurowany na każdej publicznej tabeli. Zobacz artykuł Skaner Supabase RLS dla informacji, co sprawdzić.
Następne kroki
Uruchom skanowanie FixVibe wobec swojego produkcyjnego URL — kontrola sekretów pakietu jest bezpłatna, bez rejestracji i raportuje ujawnienie service_role w mniej niż minutę. Połącz to z artykułem Skaner Supabase RLS, aby zweryfikować, że warstwa RLS wykonuje swoją pracę, i Lista kontrolna bezpieczeństwa kosza magazynu Supabase, aby zablokować dostęp do plików. Aby uzyskać tło, dlaczego narzędzia AI tak niezawodnie generują tę klasę wycieku, przeczytaj Dlaczego narzędzia kodowania AI pozostawiają luki w bezpieczeństwie.
