devto 2026-10-21 원문 보기 ↗
MCP configs are the new .env files. I scanned GitHub and found live database keys committed in public repos.
If you use Claude Desktop, Claude Code, Cursor or Windsurf with external tools, you have an .mcp.json or claude_desktop_config.json file on your machine. It tells your editor which MCP servers to start, and each server entry can carry an env block.
That env block is a secret store. And developers are pasting real keys into it — then committing the file.
I ran GitHub code search for filename:.mcp.json SUPABASE_SERVICE_ROLE and similar queries, then verified every result instead of trusting the match:
"role": "service_role". In my scan, 53 of 65 candidate files were placeholders, templates or examples. If I had emailed all 65, four out of five would have been noise — the fastest way to teach someone to ignore security reports.GET https://<ref>.supabase.co/rest/v1/ that answers 401 (no credentials sent, nothing read) proves the project is live and the key is valid until rotated.The result: 12 files in public repos contain a real, decodable service_role key. 4 of those projects are live right now. One of them was pushed the same day I scanned.
A service_role key bypasses every row-level-security policy. Whoever finds it has full read/write access to the database — not "some" access, all of it.
The mental model is wrong, not the people. .mcp.json feels like editor configuration — like settings.json, like .vscode/. Nobody expects a linter or a framework to warn them, because no tool in this ecosystem treats the file as a secret store yet.
Compare with .env: a decade of leaked keys produced pre-commit hooks, secret scanners, CI checks, and platform warnings. .mcp.json has none of that guardrail history, so the same 2015-style mistakes are being made again in 2026 — just with database keys that grant more access than most API keys ever did.
Deleting the file doesn't help either: git history keeps every version forever. The only fix that works is rotating the key (Supabase Dashboard → Settings → API → rotate), then moving the new value into a runtime environment variable.
I've spent months doing Supabase RLS audits. The configuration side of this problem needs zero-expertise tooling, so I built a family of zero-dependency auditors. Each one runs locally, reads nothing from the network, and redacts evidence in its reports so the report itself is safe to paste into an issue:
npx mcp-config-security. Scans Claude Desktop, Claude Code, Cursor and Windsurf configs for service_role JWTs (decoded, so a token hiding under a neutral key name is still caught), real-looking tokens in env/headers, --dangerously-skip-permissions flags, silent autoApprove on write tools, plaintext http:// remotes, disabled TLS verification, and filesystem servers rooted at /.npx supabase-service-role-scanner. Walks a project for NEXT_PUBLIC_*SERVICE_ROLE* variables, NEXT_PUBLIC_ values carrying non-anon JWTs, and service_role literals in source. The anon key in NEXT_PUBLIC_ is correct by design and never flagged.npx github-actions-audit. Static scan of workflow YAML for pull_request_target pwn requests, ${{ github.event.pull_request.title }}-style shell injection, hardcoded secrets, and unpinned actions.npx firebase-rules-audit. Finds allow read, write: if true;, request.auth == null grants and conditions with no auth check at all, in Firestore and Storage rules.npx n8n-workflow-audit. Exported workflow JSON often carries tokens pasted straight into HTTP Request nodes. This flags hardcoded credentials, ?apikey= in URLs and unauthenticated webhooks — while treating n8n's own credentials: { id, name } references as the correct pattern.All free, all MIT, no accounts, no telemetry. The companion repo supabase-rls-leak-demo covers the database side: a fixture that goes red on a broken RLS policy and green on the one-file fix.
The first thing I ran against my own four repos was github-actions-audit. It came back with 20 findings: every actions/checkout@v4 and actions/setup-node@v4 was a mutable tag, and none of the workflows had a permissions: block.
That is the correct experience for this class of tool — it should be slightly embarrassing. I resolved the action tags to their commit SHAs (git.ref/tags/v4), pinned all four workflows, added permissions: contents: read, and pushed. Same command now exits 0. If a security linter finds nothing on its first run against your config soup, be suspicious of the linter.
If you have an .mcp.json or claude_desktop_config.json anywhere in a repo — or a NEXT_PUBLIC_ Supabase variable you're not sure about:
npx mcp-config-security
npx supabase-service-role-scanner .
Two commands, a few seconds, zero installs. If they come back clean, you're done. If they don't, rotate first, refactor second.
I reported the live leaks I found privately — no bounties asked, just the rotation. If one of these tools catches something real for you, you can buy me a coffee, but the tools stay free either way.