Skip to content
WebCureby Swasti
Critical

Secret keys in the frontend

Payment, AI and database keys shipped to every visitor's browser.

What it is

A secret key (Stripe, Razorpay, OpenAI, a database service key) ends up in client-side code or a public repo, where anyone can copy it from the browser's dev tools.

Why fast-built apps get it

AI coding tools often make an API call work the quickest way: directly from the component. Prefixes like NEXT_PUBLIC_ or VITE_ quietly bundle the key into the JavaScript every visitor downloads.

Before and after the cure

components/Chat.tsxVulnerable
const openai = new OpenAI({
  apiKey: process.env.NEXT_PUBLIC_OPENAI_KEY, // bundled into the browser
  dangerouslyAllowBrowser: true,
});
app/api/chat/route.tsCured
// Runs on the server only; the key never reaches the browser
const openai = new OpenAI({ apiKey: process.env.OPENAI_KEY });

export async function POST(req: Request) {
  const user = await requireUser(req);   // signed-in users only
  await rateLimit(user.id, "chat");      // and not unlimited
  // ...
}

What it can cost you

  • Strangers run up your OpenAI or SMS bill overnight
  • Refunds, payouts or customer data accessed with your payment key
  • Full database access with a leaked service-role key

Check your own app

  • Open your site, press F12, search the Sources tab for “sk_”, “key” and “secret”
  • Search your repo for env variables prefixed NEXT_PUBLIC_ or VITE_ that hold secrets
  • Check your git history, not just the latest commit

The cure

  • Move every secret call behind your own server route
  • Rotate any key that was ever public; deleting the line isn't enough
  • Add secret scanning to your repo so it can't happen again

Does your app have this one?

We'll check this and every other loophole on your app, then give you the cure.

Talk to us on WhatsApp