I re-verified 30 disclosed Supabase keys. 20% were still live.
I re-verified 30 disclosed Supabase keys. 20% were still live. The uncomfortable finding: developers who thank you for reporting a leaked key — and swear they rotated it — often don't. by Cenk Kurtoglu — I audit Supab
I re-verified 30 disclosed Supabase keys. 20% were still live.
The uncomfortable finding: developers who thank you for reporting a leaked key — and swear they rotated it — often don't.
by Cenk Kurtoglu — I audit Supabase/Postgres access control for a living (github.com/cekuu35)
The setup
Over the last two months I've disclosed dozens of committed Supabase service_role keys to maintainers. The workflow: find a committed key, verify it's live, email the owner with a 2-minute fix, move on. Most replies are warm: "Wow, thank you so much — I'll get that rotated immediately."
That "immediately" kept nagging at me. What does it actually mean?
So this week I ran a systematic re-verification pass: went back through my disclosure history and re-tested every project — fresh key extraction, fresh JWT decode, fresh live check against each project's REST endpoint (a single authenticated ping, no data reads, no logins).
The numbers
Out of 30 previously-disclosed repos:
| State | Count | What it means |
|---|---|---|
| Key still committed AND still live (HTTP 200) | 6 | The database is readable/writable by anyone who finds the repo — right now |
| Key committed, project dead (endpoint paused/deleted) | 14 | Exposure inactive, but the key wakes up if the project is ever restored |
| Actually rotated and clean | 12 | The fix worked |
One in five "fixed" projects still had a working master key sitting in public code.
The pattern that worries me
The worst case isn't the silence. It's this exact sequence, which happened twice this week:
- I report a live
service_rolekey in a committed file. - The owner replies: "Wow, thank you — I'll get that rotated and removed immediately."
- Days later I re-verify: the same key, same file, still HTTP 200.
The rotation either never happened or happened to the wrong key — and the owner believes they're safe. That false confidence is more dangerous than never replying, because nobody re-checks.
One key I re-verified was issued in May — reported in October, "fixed" two days before my re-check, still live at verification time.
Why rotation fails (the ones I've seen)
- The dashboard flow confuses "rotate service key" with "regenerate anon key." Both buttons look similar; only one invalidates the leaked credential.
-
The key lives in more than one place. They remove it from
.env, forgetwrangler.jsonc,.env.backup, the MCP config, the README setup section. The app keeps working — because the app still reads the old key from somewhere else — so it feels fixed. - Git history is treated as cleanup. Deleting the file from HEAD doesn't remove the key from history. That's fine only after rotation — but people do it in the wrong order and skip rotation entirely.
The 3-step check that would have caught all six
-
Decode the committed JWT. The payload tells you
roleandrefin two seconds.role: service_role+ repo public = emergency. -
Ping, don't assume. One authenticated GET to
https://<ref>.supabase.co/rest/v1/with the key as bothapikeyandAuthorization: Bearer.200means the key is alive.401means rotation worked. Nothing else matters. - Rotate first, clean later. Rotate the JWT secret in the dashboard (invalidates the old key instantly), then update every location and commit. In that order, git history becomes harmless.
What I'm doing with this
I now offer rotation verification as a fixed-scope service: I re-test every key I've ever reported for you and hand you a two-line verdict — live or dead — with evidence. It's the difference between "I think I rotated" and "it's dead, here's the 401."
If you've ever committed a Supabase key: run step 2 above right now. It's one request. If you get a 200, rotate — the dashboard takes two minutes and the alternative is a stranger with full read/write on your database.
Cenk Kurtoglu — cenkkurtoglu.com · github.com/cekuu35. I disclose leaked keys for free, permanently — no payment required for the report itself.
Originally published by Dev.to Security. Aggregated on AIWithGhost for educational purposes — full credit and traffic to the original publisher.