DEV Community

Cover image for How an Unrestricted Firebase API Key Can Drain Your Bank Account Overnight
Alan West
Alan West

Posted on

How an Unrestricted Firebase API Key Can Drain Your Bank Account Overnight

You know that Firebase API key sitting in your client-side JavaScript? The one the docs told you was safe to expose? Yeah, it might be a ticking time bomb.

A developer recently shared on the Google AI Developer Forum that they were hit with a 54,000+ euro bill in just 13 hours. The culprit wasn't some sophisticated breach. It was a single Firebase browser key without API restrictions — and an attacker who knew exactly what to do with it.

Let me walk you through what went wrong, why it's more common than you'd think, and how to make sure this doesn't happen to you.

The Root Cause: Firebase Keys Aren't as "Safe" as You Think

Here's the thing most developers get wrong about Firebase. When you create a Firebase project, it auto-generates API keys for you. These keys get embedded directly in your client-side config:

// This object lives in your frontend code — it's PUBLIC
const firebaseConfig = {
  apiKey: "AIzaSy...",      // this is the key we're talking about
  authDomain: "myapp.firebaseapp.com",
  projectId: "myapp-12345",
  storageBucket: "myapp-12345.appspot.com",
  messagingSenderId: "123456789",
  appId: "1:123456789:web:abc123"
};
Enter fullscreen mode Exit fullscreen mode

Firebase's own documentation has long said this key is designed to be public. And for pure Firebase services, that's mostly true — Firestore, Realtime Database, and Storage all rely on Security Rules for access control, not the API key itself.

But here's where it gets dangerous. That API key is actually a Google Cloud API key. And Google Cloud API keys, by default, are unrestricted — meaning they can call any enabled API on the project.

The Attack Vector: Gemini API Rides In on Your Firebase Key

This is the detail that burned the developer. When the Gemini API was enabled on their Firebase project, all existing API keys on that project automatically gained access to the Gemini endpoint. No notification. No confirmation dialog. No email warning.

Think about that for a second. You enable a new paid API — maybe you're just testing it out — and suddenly every browser key you've ever shipped in production JavaScript can make calls to expensive generative AI models.

An attacker finds your key (it's in your page source, your JavaScript bundle, maybe even a public GitHub repo), and starts hammering the Gemini endpoint. At AI-model pricing, it doesn't take long to rack up five figures.

The developer's 54,000 euros accrued in 13 hours. Others have reported similar incidents — bills of $82,000 and $15,000 from the same attack pattern.

How Widespread Is This?

Pretty widespread, unfortunately. TruffleSecurity (the folks behind TruffleHog) found over 2,800 publicly exposed Google API keys that were vulnerable to this exact scenario — unrestricted keys attached to projects where paid APIs were enabled.

Those keys were sitting in public repos, client-side bundles, and Firebase configs. Each one a potential five-figure invoice waiting to happen.

Step-by-Step Fix: Locking Down Your API Keys

Here's exactly what you should do right now. Not tomorrow. Now.

1. Audit Every Key on Your Project

Head to the Google Cloud Console:

Google Cloud Console → APIs & Services → Credentials
Enter fullscreen mode Exit fullscreen mode

You'll see every API key associated with your project. For each one, check two things:

  • Application restrictions (where the key can be used from)
  • API restrictions (which APIs the key can call)

If either says "None" or "Don't restrict key" — you've got a problem.

2. Restrict Firebase Browser Keys to Firebase APIs Only

Edit your Firebase browser key and set API restrictions to only the APIs Firebase actually needs:

  • Firebase Installations API
  • Firebase Auth API (Identity Toolkit)
  • Cloud Firestore API
  • Firebase Realtime Database
  • Cloud Storage for Firebase
  • Firebase Cloud Messaging

Strip everything else. Your Firebase SDK doesn't need access to Gemini, Maps, Translate, or any other paid API.

3. Add Application Restrictions

For browser keys, set HTTP referrer restrictions to your actual domains:

Allowed referrers:
  myapp.com/*
  www.myapp.com/*
  localhost:3000/*    // for local dev, remove in production key
Enter fullscreen mode Exit fullscreen mode

For mobile apps, use Android/iOS app restrictions with your package name and SHA-1 fingerprint.

4. Use Separate Keys for Separate Purposes

Never reuse your Firebase browser key for server-side work. Create dedicated keys:

# Create a restricted key specifically for Gemini (server-side only)
gcloud services api-keys create \
  --display-name="gemini-server-key" \
  --api-target=service=generativelanguage.googleapis.com

# The key output will be restricted to Gemini only
# Use this server-side, NEVER in client code
Enter fullscreen mode Exit fullscreen mode

5. Disable APIs You're Not Using

If you enabled the Gemini API to experiment and forgot about it:

# List enabled APIs on your project
gcloud services list --enabled

# Disable what you don't need
gcloud services disable generativelanguage.googleapis.com
Enter fullscreen mode Exit fullscreen mode

Every enabled API is an additional attack surface. Keep the list minimal.

6. Set Up Billing Alerts and Quotas

This won't prevent the attack, but it can limit the blast radius:

# Set per-key quota for an API (example: 100 requests/min)
# Do this in Cloud Console → APIs & Services → your API → Quotas
Enter fullscreen mode Exit fullscreen mode

Set billing alerts at multiple thresholds — I'd suggest 50%, 100%, and 200% of your expected monthly spend. Google Cloud doesn't offer hard spending caps on most services, so quotas and alerts are your best bet.

For AI APIs Specifically: Skip API Keys Entirely

If you're building something with Gemini or any other AI model API, don't use API keys for production workloads at all. Use service accounts with IAM roles instead:

# Instead of passing an API key, authenticate with a service account
import google.auth
from google.auth.transport.requests import Request

credentials, project = google.auth.default(
    scopes=["https://www.googleapis.com/auth/generative-language"]
)

# credentials are tied to IAM roles — much more granular control
# and they NEVER get embedded in client-side code
Enter fullscreen mode Exit fullscreen mode

Service accounts give you IAM-level control, audit logging, and they can't be accidentally leaked in a JavaScript bundle.

The Bigger Problem: Implicit Access Expansion

What really bothers me about this incident isn't the missing restrictions — it's the silent access expansion. Enabling a new API on a project shouldn't automatically grant all existing keys access to it without any warning. That's a platform design issue.

Google has acknowledged the problem and published a remediation roadmap, which reportedly includes auto-restricting existing Firebase keys and changing defaults for new projects. That's good. But if you're reading this and you have a Firebase project in production, don't wait for Google to fix it.

Go check your keys. It takes five minutes. It could save you 54,000 euros.

Top comments (0)