Google API keys & GCP service-account JSON in the open
Two very different things called 'Google credentials'
People lump Google credentials together, but two common ones carry wildly different risk, and confusing them is how the worse one leaks. Let us separate them first.
The Google API key
A Google API key is a single string — something like AIza... — that authorizes requests to a particular Google service: Maps, the YouTube data API, and so on. Some Google APIs are designed to be called from the browser, so the key being visible is not automatically a disaster. But that is only true if the key is restricted.
An unrestricted API key is a problem. If anyone can copy it and use it from their own site, they can run up usage and bills against your account, or abuse quota-limited services on your dime. The defense is restriction:
- Application restrictions — lock the key to your specific HTTP referrers (your domains) or IP ranges.
- API restrictions — limit the key to only the exact APIs it needs, nothing else.
A tightly restricted Maps key in your frontend is acceptable. A wide-open one is a liability even though it 'belongs' in the browser.
The GCP service-account JSON
This is the dangerous one, and the two should never be confused. A GCP service-account key is a JSON file containing a private key and an identity. It is meant for server-to-server authentication — your backend proving it is allowed to talk to Google Cloud. It often carries broad permissions: read and write to storage buckets, query databases, manage resources.
That file is a master key. It has the telltale shape — fields like "type": "service_account", "private_key", "client_email" — which makes it instantly recognizable to scanners. If it leaks, whoever holds it can authenticate as that identity and do whatever its roles allow. There is no 'but it is restricted to my domain' here; a service-account JSON has no business being anywhere near the browser, ever.
Where both leak
The same tired paths catch them:
- In the frontend bundle — a service-account JSON imported into client code, or an unrestricted API key hardcoded there.
- Committed to a repo — the JSON file checked in, then the repo goes public or is forked.
- In a public folder — the file deployed as a static asset, one predictable URL away.
- In logs or error traces — the credential printed during a failure.
How to lock them down
- Restrict every API key by referrer/IP and by API. Treat an unrestricted key as already abused.
- Keep service-account JSON server-side only, in a secrets manager or environment configuration — never in code that ships, never in a public folder.
- Prefer keyless auth where available — workload identity and similar mechanisms avoid having a long-lived JSON key at all.
- Rotate anything exposed. Delete the leaked key in the Google console, issue a new one, and check usage logs for activity you do not recognize.
Check what your site exposes
A service-account JSON or an unrestricted key buried in a bundle is invisible to a quick look. An automated scan reads your pages and scripts the way a bot would and flags Google keys and service-account credentials reachable from the browser. Scan your site and find out before someone bills your cloud account.