Skip to main content
All rules

SEC-016

Secret key exposed in public HTML or JavaScript

CriticalDefault severity · Security · Secrets, HTTPS and security headers

Last reviewed

A secret key in your public HTML or JavaScript can be copied by anyone who opens your site and used to charge your payment account, run up AI bills or read your data. Rotate the key at the provider immediately, then move the code that uses it to a server route that keeps the key in a server-only environment variable.

In apps built with AI tools

AI tools make the quickest change that works, and calling an API straight from the browser works. The key usually arrives through a public environment prefix (NEXT_PUBLIC_, VITE_, EXPO_PUBLIC_) or is pasted into a component while debugging, and stays there after the feature ships.

What it checks

Known key formats (payment, AI provider, cloud, GitHub, email, Supabase service role, private keys) in the page and its scripts.

Why it matters

Anyone can copy the key from your JavaScript and run up charges, read data or act as your app.

Recommended fix

Rotate the key now, move the call that needs it to a server route, and keep the key in a server-only environment variable.

How to verify

Rotate the key with the provider, redeploy, then search the built JavaScript for the key prefix and confirm it is gone.

Example fix prompt

Generic prompt (your report adds the evidence)
Please fix this production issue in my app.

Issue SEC-016: Secret key exposed in public HTML or JavaScript
Site: https://your-app.example

First confirm the issue in the code or live config. If it is not present, say so and change nothing.
Task: A secret key is bundled into client-side code (see evidence for the key type and file). Move every call that uses it into a server-side API route or server action, read the key from a server-only environment variable (never NEXT_PUBLIC_/VITE_ prefixed), and make the client call that route instead. Do not paste the key itself anywhere.

Constraints: make the smallest change that fixes this, keep the current design and behaviour, never paste secret values into code or chat, and tell me which files you changed.
Done when: Rotate the key with the provider, redeploy, then search the built JavaScript for the key prefix and confirm it is gone.

Questions

Is deleting the key from the code enough?
No. Old bundles are cached and the key may already be copied. Rotate it first, then remove it.
Which keys are fine in the browser?
Keys designed for it: Stripe publishable keys, Supabase anon or publishable keys, Firebase web config and domain-restricted map keys.

References