· 7 min read ·

When 'Firebase Keys Are Safe to Be Public' Runs Into the Gemini API

Source: hackernews

A developer posted to Google’s AI developer forum this week with a straightforward nightmare: a €54,000 billing spike, accumulated over 13 hours, traced back to a Firebase browser key with no API restrictions. Someone had found the key in client-side JavaScript, recognized it had access to the Generative Language API, and hammered Gemini endpoints until the bill hit five figures. The original thread has collected hundreds of comments, most of them from developers who have been in some version of this situation before.

The incident is easy to read as a simple failure to follow security basics. That reading misses something important about how Firebase is designed and documented, and why Gemini specifically turns an old vulnerability into a new financial catastrophe.

The Belief That Gets People Into Trouble

Firebase documentation has long stated that the Web API key, the one that appears in your firebaseConfig object and ships in every client-side bundle, is safe to be public. This is technically accurate for Firebase-protected services. Firestore, Realtime Database, Firebase Storage, and Firebase Authentication are all guarded by Security Rules, which are evaluated server-side regardless of who holds the API key. An attacker with your Firebase browser key cannot read your Firestore data if your rules are written correctly. The key is not the authorization mechanism; the rules are.

The problem is that this characterization does not generalize to the rest of Google Cloud. Firebase projects are Google Cloud projects. Every Google API you enable on that project is potentially callable with that same browser key, unless you explicitly restrict it. Maps JavaScript API, Cloud Translation API, YouTube Data API, and now the Generative Language API (Gemini) have no Firebase Security Rules. They use the API key itself as the access control boundary. Leave it unrestricted, and anyone who finds the key in your JavaScript bundle can call those APIs on your dime.

Firebase’s own documentation has been updated over the years to include warnings about this, but the central claim, that the key is safe to be public, gets repeated as shorthand in tutorials, Stack Overflow answers, and forum threads without the qualifying context. Developers who learn Firebase first and Google Cloud second often never encounter the distinction.

API Key Restrictions Are the Actual Defense

Google Cloud Console gives you two categories of restriction for any API key, including Firebase’s auto-created browser key. The first category is application restrictions: you can limit the key to requests with specific HTTP referrers, specific IP addresses, or specific Android/iOS app identifiers. For a browser key, the HTTP referrer option lets you whitelist your own domain. The second category is API restrictions: you select exactly which Google APIs the key is permitted to call.

For a typical Firebase web application, the API restriction list should include only the Firebase-specific APIs: Identity Toolkit API, Firebase Installations API, Cloud Firestore API if you are using Firestore, Firebase Cloud Messaging API if you use push notifications. Nothing else. You navigate to this configuration at Google Cloud Console, under APIs & Services, then Credentials, click the browser key, and set it there.

HTTP referrer restrictions are worth applying but should not be treated as a primary defense. A request that omits or spoofs the Referer header bypasses them entirely, and most abuse tooling does exactly that. The API restrictions list, which controls which services the key can call at all, is what actually limits exposure.

The Firebase console does not surface this configuration directly. You have to know to go to the underlying Google Cloud Console to find it. That indirection contributes to the problem: developers who live in the Firebase console may never see the Credentials page in their Google Cloud project.

Why Gemini Makes This Worse Than Maps API Abuse

Firebase browser key abuse is not new. The same class of attack has been running against Maps JavaScript API and Cloud Translation API for years. Developers have posted bills of $10,000 to $50,000 from Maps API abuse through unprotected Firebase keys going back to at least 2018. Google added automated key scanning partnerships and updated documentation in response, but the underlying architecture remained unchanged.

Gemini creates a higher financial ceiling for abuse. Gemini 1.5 Pro pricing runs at roughly $3.50 per million input tokens and $10.50 per million output tokens for context windows under 128K. An automated abuse script making concurrent requests with large context windows can move through tokens at a rate that Maps API geocoding calls cannot approach. The €54,000 figure from 13 hours of abuse is consistent with high-concurrency large-output requests against a Pro-tier model. Maps API abuse tends to plateau because geocoding requests are cheap and there are natural rate ceilings. Token-based billing at scale does not have the same natural plateau.

For developers who have been using AI Studio to prototype with Gemini, the default onboarding path generates an API key through the standard Google Cloud key infrastructure. If you then connect that to a Firebase project with an existing billing account, you have an unrestricted browser key on a project that has the Generative Language API enabled and a payment method attached. The attack surface is set up by the default tooling.

Google Cloud Has No Hard Spending Cap

This is the part of these incidents that generates the most frustration. Google Cloud’s budget system lets you set alerts at configurable thresholds, 50%, 90%, 100% of a monthly budget, and sends you email when they trigger. Those alerts do not stop spending. They are notifications, not circuit breakers.

Google’s explicit position is that automatically cutting off billing could cause production outages and is therefore a decision that should be made by the account owner, not the platform. The documentation offers a workaround: a Cloud Function triggered by a Pub/Sub budget alert message that calls the Cloud Billing API to remove the billing account from the project, effectively disabling all paid services.

exports.stopBilling = async (pubsubEvent, context) => {
  const projectId = process.env.GOOGLE_CLOUD_PROJECT;
  const billingClient = new CloudBillingClient();
  await billingClient.updateProjectBillingInfo({
    name: `projects/${projectId}`,
    projectBillingInfo: { billingAccountName: '' }
  });
};

This works, and Google links to it in their budget management documentation. The friction involved in setting it up, writing a Cloud Function, configuring Pub/Sub, assigning the right IAM roles, means most developers who need it most are the least likely to have it deployed. Platforms like Vercel and Netlify offer hard spend caps as a first-class UI feature. Google Cloud’s version requires infrastructure work.

Quota Limits as a Low-Effort Defense

An underused mitigation: you can lower your own project’s quota limits for individual APIs. For the Generative Language API, you can reduce the requests-per-minute and tokens-per-minute quotas to values appropriate for your actual usage. A solo developer building a personal app has no legitimate need for 1,000 requests per minute to Gemini. Setting the quota to 10 or 20 does not affect normal usage but caps the blast radius of key abuse dramatically.

This is configured through APIs & Services, Enabled APIs, selecting the Generative Language API, then Quotas. You can submit a quota reduction request, which is processed quickly for reductions. Combined with API restrictions and referrer restrictions, it creates multiple independent layers of containment.

The Right Architecture for AI APIs

For any production application calling an AI API, the correct design is a server-side proxy. The API key lives on your server, never in the client bundle. Your backend validates the user’s identity, using Firebase Auth ID tokens if you are in the Firebase ecosystem, before proxying the request to Gemini or any other AI endpoint.

Browser → Your backend (validates Firebase Auth JWT) → Gemini API

This pattern gives you per-user rate limiting, request logging, cost attribution, and the ability to revoke or rotate keys without touching client code. It is also the path that Google’s own enterprise product, Vertex AI, effectively enforces. Vertex AI uses service account authentication rather than API keys, which means it requires server-side OAuth2 flows and cannot be called directly from a browser without explicit user delegation. The architecture constraint is built into the product.

The AI Studio API key path, which uses generativelanguage.googleapis.com, is designed for prototyping. The documentation positions Vertex AI as the production path. Most developers using Firebase for their application layer are reaching for the AI Studio key because the setup is simpler and the documentation for combining Firebase with Vertex AI involves more steps.

What to Actually Do Right Now

If you have a Firebase project with any AI API enabled, three things are worth doing immediately. Go to Google Cloud Console, find your browser key under Credentials, and add an API restrictions list that excludes everything you are not intentionally using from the browser. Check which APIs are enabled on your project under Enabled APIs and Services, and disable any you do not recognize or no longer need. Then lower your quota limits for any high-billing APIs to something that reflects your actual usage.

The browser key being public is a Firebase design decision that works for Firebase services. Everything built on top of that project is a separate question, and Gemini’s billing model means the consequences of leaving it unaddressed are proportionally larger than they were before large language models entered the picture.

Was this interesting?