Back to Blog
critical SEVERITY10 min read

How Hardcoded Firebase API Keys Happen in JavaScript Service Workers and How to Fix Them

A production Firebase service worker file (`static/firebase-messaging-sw.js`) contained hardcoded API keys and project configuration directly in publicly accessible JavaScript — including a second, previously commented-out set of credentials from an older project. While Firebase web config values are technically public identifiers, pairing them with unrestricted Firebase projects or missing Security Rules turns them into an open door for push notification abuse, quota exhaustion, and unauthorize

O
By Orbis AppSec
Published September 5, 2026Reviewed September 5, 2026

Answer Summary

This vulnerability is a hardcoded-secrets issue (CWE-312/CWE-798) in a JavaScript Firebase service worker, where live Firebase API keys and project identifiers were embedded directly in a publicly served file (`static/firebase-messaging-sw.js`). A second set of commented-out credentials from a legacy project was also present. The fix removes the stale credentials, adds inline documentation explaining Firebase's public-key design contract, and emphasizes that these values must always be paired with HTTP referrer restrictions, Firebase Security Rules, and App Check enforcement to prevent unauthorized access.

Vulnerability at a Glance

cweCWE-312 (Cleartext Storage of Sensitive Information) / CWE-798 (Use of Hard-coded Credentials)
fixRemoved stale commented-out credentials, added inline security guidance requiring API key restrictions and Firebase Security Rules enforcement
riskUnauthorized Firebase service access, push notification abuse, quota exhaustion
languageJavaScript
root causeLive and legacy Firebase config values hardcoded in a publicly served service worker file without access restrictions
vulnerabilityHardcoded Firebase API Keys / Credential Exposure

How Hardcoded Firebase API Keys Happen in JavaScript Service Workers and How to Fix Them


The Vulnerability at a Glance

Field Detail
Vulnerability Hardcoded Firebase API Keys / Credential Exposure
CWE CWE-312 · CWE-798
Language JavaScript
Risk Push notification abuse, quota exhaustion, unauthorized Firebase access
Root Cause Live and legacy Firebase config values hardcoded in a publicly served service worker
Fix Removed stale credentials, added security guidance enforcing API restrictions and Security Rules

Summary

A production Firebase service worker file (static/firebase-messaging-sw.js) contained hardcoded API keys and project configuration directly in publicly accessible JavaScript — including a second, previously commented-out set of credentials from an older project. While Firebase web config values are technically public identifiers by design, pairing them with unrestricted Firebase projects or absent Security Rules turns them into an open door for push notification abuse, quota exhaustion, and unauthorized data access.


Introduction

The static/firebase-messaging-sw.js file is the backbone of push notification delivery in this application. It initializes Firebase, registers a messaging service worker, and handles incoming messages in the browser background. But buried inside the firebaseConfig object at line 6, the file held something it shouldn't: not one, but two sets of Firebase project credentials — one active, one commented out from what appears to be a previous project (asseto-push-notification).

The active configuration looked like this:

const firebaseConfig = {
  apiKey: 'AIzaSyBiUQoc2jSnM8Et_908_Jcj75RTz1IGgco',
  appId: '1:373301044674:android:e331be3913f69c4518225b',
  messagingSenderId: '373301044674',
  // ...
};

And the commented-out legacy credentials were sitting right above it:

//     apiKey: "AIzaSyBdBIbK2e1JDbZGKKXU6yFwL1ze0jprq6Y",
//     authDomain: "asseto-push-notification.firebaseapp.com",
//     projectId: "asseto-push-notification",
//     storageBucket: "asseto-push-notification.firebasestorage.app",
//     messagingSenderId: "600702361358",
//     appId: "1:600702361358:web:58cb3c2ee78e9b1280415f",
//     measurementId: "G-R0J26HTEVL"

This pattern — live credentials alongside rotated-but-not-deleted legacy credentials — is one of the most common and underappreciated security hygiene failures in frontend development.


The Vulnerability Explained

Why Firebase API Keys Are "Public by Design" (and Why That Doesn't Mean "Safe")

Google's Firebase documentation explicitly states that the apiKey in a web configuration is a public identifier, not a secret. It's used to identify your Firebase project to Google's servers, not to authenticate privileged access. This is fundamentally different from, say, an AWS secret key or a database password.

However, this design contract comes with a critical obligation that is easy to overlook: the API key is only safe if your Firebase project is properly locked down. Specifically:

  1. Firebase Security Rules must be configured to deny unauthorized reads/writes to Firestore, Realtime Database, and Cloud Storage.
  2. The API key must be restricted in Google Cloud Console — limited to specific HTTP referrers (your domain) and specific APIs (only Firebase-related ones).
  3. Firebase App Check should be enabled to ensure only your legitimate app can call Firebase services.

Without these controls, anyone who extracts the apiKey, appId, and messagingSenderId from this publicly served JavaScript file can:

  • Send arbitrary push notifications to your users via the Firebase Cloud Messaging (FCM) API using the messagingSenderId
  • Exhaust your Firebase quota, causing service disruption or unexpected billing
  • Access or modify data in Firestore/Realtime Database if Security Rules are permissive
  • Abuse Firebase Authentication to create accounts or enumerate users if auth is misconfigured

The Commented-Out Credentials Are Still a Problem

The legacy asseto-push-notification project credentials weren't just a cosmetic issue. Comments in JavaScript are not stripped before the file is served to clients. Every browser that downloads firebase-messaging-sw.js receives the full text, including:

//     apiKey: "AIzaSyBdBIbK2e1JDbZGKKXU6yFwL1ze0jprq6Y",
//     authDomain: "asseto-push-notification.firebaseapp.com",

These values are:
- Indexed by search engines if the file is publicly crawlable
- Captured in version control history and potentially exposed in public repositories
- Visible to anyone who opens DevTools in a browser

If the asseto-push-notification project still exists and hasn't had its Security Rules hardened or its API key restricted, those commented-out credentials represent a live attack surface against a different Firebase project.

Concrete Attack Scenario

An attacker discovers firebase-messaging-sw.js through a routine scan of the application's JavaScript assets. They extract messagingSenderId: '373301044674' and the apiKey. Using the Firebase Admin SDK or a direct HTTP call to the FCM v1 API, they craft a push notification payload and send it to all subscribed devices. With no App Check enforcement and an unrestricted API key, the request succeeds. Users receive fraudulent push notifications appearing to come from the legitimate application — a perfect vector for phishing.


The Fix

The pull request made two targeted changes to static/firebase-messaging-sw.js:

1. Removed the Commented-Out Legacy Credentials

Before:

const firebaseConfig = {
//     apiKey: "AIzaSyBdBIbK2e1JDbZGKKXU6yFwL1ze0jprq6Y",
//     authDomain: "asseto-push-notification.firebaseapp.com",
//     projectId: "asseto-push-notification",
//     storageBucket: "asseto-push-notification.firebasestorage.app",
//     messagingSenderId: "600702361358",
//     appId: "1:600702361358:web:58cb3c2ee78e9b1280415f",
//     measurementId: "G-R0J26HTEVL"
    apiKey: 'AIzaSyBiUQoc2jSnM8Et_908_Jcj75RTz1IGgco',
    appId: '1:373301044674:android:e331be3913f69c4518225b',
    messagingSenderId: '373301044674',

After:

// NOTE: Firebase web config values are public identifiers by design (see
// https://firebase.google.com/docs/projects/api-keys). They must never be paired
// with an unrestricted Firebase project - restrict this API key (HTTP referrer /
// API restrictions in Google Cloud Console) and enforce Firebase Security Rules /
// App Check server-side so possession of these values alone grants no access.
  const firebaseConfig = {
    apiKey: 'AIzaSyBiUQoc2jSnM8Et_908_Jcj75RTz1IGgco',
    appId: '1:373301044674:android:e331be3913f69c4518225b',
    messagingSenderId: '373301044674',

The seven commented-out lines containing the asseto-push-notification project credentials are completely removed, eliminating the legacy exposure surface.

2. Added Inline Security Documentation

The fix adds a multi-line comment above firebaseConfig that does something important: it encodes the security contract directly in the code. Future developers reading this file now see an explicit reminder that:

  • Firebase API keys are public identifiers by design (with a link to the official docs)
  • The API key must be restricted in Google Cloud Console
  • Firebase Security Rules and App Check must be enforced server-side
  • Possession of these values alone should grant no access

This is defensive documentation — it prevents the next developer from assuming "it's fine, these are meant to be public" without understanding the full obligation that comes with that design.

What This Fix Does NOT Do

It's worth being explicit: this fix does not rotate the active apiKey. The AIzaSyBiUQoc2jSnM8Et_908_Jcj75RTz1IGgco value remains in the file. The fix correctly recognizes that rotating a Firebase web API key without simultaneously hardening the project's Security Rules and key restrictions would provide no real security improvement — the new key would be just as exposed.

The real fix is the combination of:
1. Removing legacy credentials ✅ (done in this PR)
2. Adding documentation of the security contract ✅ (done in this PR)
3. Restricting the API key in Google Cloud Console ⚠️ (must be done manually)
4. Enforcing Firebase Security Rules ⚠️ (must be verified/enforced separately)
5. Enabling Firebase App Check ⚠️ (recommended additional step)


Prevention & Best Practices

Restrict Your Firebase API Key Immediately

In Google Cloud Console → APIs & Services → Credentials, find your Firebase API key and apply:

  • Application restrictions: HTTP referrers — add only your production domain(s) (e.g., https://yourdomain.com/*)
  • API restrictions: Restrict to only the Firebase APIs your app actually uses (Firebase Installations API, FCM, etc.)

This ensures that even if someone extracts the key from your JavaScript, requests from unauthorized origins will be rejected by Google's infrastructure.

Write Strict Firebase Security Rules

A default Firebase project often ships with open rules:

// DANGEROUS default - never use in production
rules_version = '2';
service cloud.firestore {
  match /databases/{database}/documents {
    match /{document=**} {
      allow read, write: if true; // ← anyone can read/write everything
    }
  }
}

Replace these with rules that verify authentication and authorization:

rules_version = '2';
service cloud.firestore {
  match /databases/{database}/documents {
    match /users/{userId} {
      allow read, write: if request.auth != null && request.auth.uid == userId;
    }
  }
}

Enable Firebase App Check

Firebase App Check ensures that only your legitimate app instances can access your Firebase backend. Even if an attacker extracts your config values, App Check attestation (using reCAPTCHA v3 for web) blocks unauthorized clients.

Never Leave Commented-Out Credentials in Source Code

Treat commented-out credentials the same as live credentials. If you're rotating keys or migrating projects:

  1. Delete the old credentials from the file entirely
  2. Rotate the old key in the Firebase/Google Cloud Console
  3. Verify the old key is revoked before merging

Use a pre-commit hook or CI check with a tool like Gitleaks or Trufflehog to catch credential patterns before they reach your repository.

Use Environment Variables for Build-Time Injection

While Firebase web config is designed to be public, a defense-in-depth approach injects these values at build time rather than hardcoding them:

// In your build process (e.g., webpack, Vite)
const firebaseConfig = {
  apiKey: process.env.VITE_FIREBASE_API_KEY,
  appId: process.env.VITE_FIREBASE_APP_ID,
  messagingSenderId: process.env.VITE_FIREBASE_MESSAGING_SENDER_ID,
};

This doesn't prevent the values from appearing in the built output, but it:
- Keeps credentials out of version control
- Makes it easier to use different configs per environment
- Reduces the risk of accidentally committing the wrong project's credentials

OWASP and CWE References

This vulnerability maps to:
- CWE-312: Cleartext Storage of Sensitive Information
- CWE-798: Use of Hard-coded Credentials
- OWASP A02:2021 – Cryptographic Failures (storing/exposing credentials without protection)
- OWASP A05:2021 – Security Misconfiguration (Firebase project without proper restrictions)


Key Takeaways

  • Commented-out credentials in firebase-messaging-sw.js are still served to every client — JavaScript comments are not stripped before delivery, making them fully visible in DevTools and to web crawlers.
  • Firebase's "public by design" API key model is only safe when paired with key restrictions and Security Rules — the apiKey in firebaseConfig is not inherently safe; it requires active configuration in Google Cloud Console to be safe.
  • The messagingSenderId: '373301044674' value enables FCM abuse — with an unrestricted key and no App Check, this value alone is enough to attempt unauthorized push notification delivery.
  • Legacy project credentials (asseto-push-notification) must be rotated, not just commented out — if that Firebase project still exists, those seven commented-out lines represent a live attack surface against a different application.
  • Inline security documentation prevents regression — the added comment block ensures future developers understand the security contract before modifying the Firebase configuration.

How Orbis AppSec Detected This

  • Source: The firebaseConfig object in static/firebase-messaging-sw.js line 6, where Firebase project credentials are assigned as string literals directly in the JavaScript source
  • Sink: The publicly served service worker file itself — any client downloading firebase-messaging-sw.js receives the full credential set, including commented-out legacy values
  • Missing control: No API key restrictions in Google Cloud Console, no documented requirement for Firebase Security Rules enforcement, and no removal of legacy asseto-push-notification credentials that remained in commented form
  • CWE: CWE-312 (Cleartext Storage of Sensitive Information), CWE-798 (Use of Hard-coded Credentials)
  • Fix: Removed the seven commented-out legacy credential lines and added an inline comment block documenting the Firebase public-key security contract and required hardening steps

Orbis AppSec automatically detected this vulnerability and opened a pull request with the fix. Try Orbis AppSec on your repositories to find and fix issues like this automatically.


Conclusion

The static/firebase-messaging-sw.js vulnerability is a textbook example of how a "safe by design" feature becomes unsafe through incomplete implementation. Firebase web API keys are public identifiers — but only when the surrounding security controls (key restrictions, Security Rules, App Check) are in place. When they're not, those public identifiers become the keys to your Firebase kingdom.

The fix here is surgical and correct: remove the legacy credentials that had no business being in the file, and add documentation that makes the security contract explicit for every developer who touches this code in the future. But the work doesn't stop at the PR merge — the API key restriction and Security Rules enforcement must happen in the Firebase and Google Cloud consoles to complete the security picture.

If your codebase uses Firebase, take ten minutes today to audit your firebaseConfig usage: Are your API keys restricted? Are your Security Rules tight? Is App Check enabled? The answers to those three questions determine whether your Firebase configuration is a harmless public identifier or an open invitation.


References

Frequently Asked Questions

What is a hardcoded Firebase API key vulnerability?

It occurs when Firebase project credentials (apiKey, appId, messagingSenderId, etc.) are embedded directly in source code or publicly served files. If the Firebase project lacks proper Security Rules or API key restrictions, anyone who finds these values can abuse the project's services.

How do you prevent Firebase credential exposure in JavaScript?

Restrict the API key in Google Cloud Console using HTTP referrer and API restrictions, enforce Firebase Security Rules server-side, enable Firebase App Check, and never leave stale credentials from previous projects commented out in production files.

What CWE is hardcoded Firebase credential exposure?

It maps to CWE-312 (Cleartext Storage of Sensitive Information) for the storage/transmission of credentials in plaintext, and CWE-798 (Use of Hard-coded Credentials) for embedding credentials directly in code.

Is commenting out old credentials enough to prevent exposure?

No. Commented-out credentials in JavaScript files are still served to any client that downloads the file, are indexed by search engines, and are captured in version control history. They must be fully removed from the codebase and rotated if they were ever live.

Can static analysis detect hardcoded Firebase keys?

Yes. Tools like Semgrep, Gitleaks, and multi-agent AI scanners (like Orbis AppSec) can detect Firebase API key patterns (`AIzaSy...`) in source files and flag them for review, even when commented out.

View the Security Fix

Check out the pull request that fixed this vulnerability

View PR #265

Related Articles

critical

How hardcoded API key exposure happens in Node.js plugins and how to fix it

A critical hardcoded API key (`actor-studio-gpt-beta`) was discovered in the `src/plugins/llm/index.js` file of the Actor Studio application, granting anyone with source code access the ability to make unauthorized requests to the LLM service endpoints. The fix removes the default key from both the LLM class definition and the settings module, requiring the key to be explicitly configured through module settings instead.

high

How Insecure Credential Storage Happens in Node.js and How to Fix It

A critical vulnerability in the Google Vision translator module stored API keys in plaintext configuration files accessible to attackers with local filesystem access. The fix relocates the API key from the URL query parameter to a secure HTTP header, eliminating the exposure vector while maintaining full functionality.

critical

How API Key Exposure in URL Query Parameters Happens in Node.js and How to Fix It

A critical security vulnerability was discovered in the `lib/crux.js` file where the CrUX API key was being transmitted as a URL query parameter instead of using secure HTTP headers. This exposed the API key in server logs, proxy logs, browser history, and network monitoring tools. The fix moves the API key to the `X-Goog-Api-Key` header, preventing credential leakage across logging systems.

critical

How API Key Exposure and Unsafe Process Spawning Happens in Node.js Scripts and How to Fix It

A critical security vulnerability in the `scripts/close-issues.mjs` file exposed API key patterns in documentation and used unsafe `spawnSync` calls to execute curl commands. The fix replaces dangerous process spawning with native `fetch()` API calls and removes sensitive configuration examples from documentation, eliminating both credential exposure and command injection risks.

critical

How hardcoded API credentials in client-side JavaScript happens in userscripts and how to fix it

The jhs-enhance.user.js userscript contained a hardcoded Imgur API Client-ID embedded directly in client-side JavaScript code, exposing it to anyone who installed or viewed the script source. This critical vulnerability allowed unauthorized users to extract and abuse the API credentials for unlimited image uploads. The fix replaced the hardcoded credential with a user-prompt mechanism that requires each user to provide their own Imgur Client-ID.

critical

How Information Disclosure Vulnerabilities Happen in Python APIs and How to Fix It

A critical information disclosure vulnerability in the Hermes plugin dashboard API was exposing sensitive filesystem paths, credential file locations, and configuration details without authentication. The fix redacts this sensitive information from API responses, replacing absolute paths with boolean status indicators to prevent attackers from locating and targeting credential files.