Secure a FlutterFlow app by layering defenses: write strict Firestore Security Rules so users can only access their own data, rely on Firebase's default TLS and encryption at rest for data in transit and storage, schedule automated Firestore backups, minimize what you store, and never store raw payment details — use Stripe tokens instead.
Layered Data Security for FlutterFlow Apps
Security in a FlutterFlow app is not a single switch — it is a stack of layers. This tutorial covers each layer in order of importance. First, Firestore Security Rules are your primary defense: a misconfigured rule is the most common cause of data breaches in FlutterFlow apps. Second, Firebase automatically encrypts all data at rest and in transit using AES-256 and TLS 1.3 — you do not have to configure this, but you should verify it is not disabled. Third, scheduled Firestore exports to Cloud Storage give you point-in-time recovery. Fourth, data minimization reduces your attack surface — do not store fields you do not need. Finally, Stripe handles payment data so you never touch raw card numbers.
Prerequisites
- FlutterFlow project with Firebase connected and Authentication enabled
- Firebase Console access with Owner or Editor role
- Firestore database with at least one collection containing user data
- Basic understanding of FlutterFlow's Firestore panel
Step-by-step guide
Audit and Rewrite Your Firestore Security Rules
Audit and Rewrite Your Firestore Security Rules
Open the Firebase Console, select your project, navigate to Firestore Database, and click the Rules tab. If your rules contain allow read, write: if true; — your database is completely open to the public internet. Replace this immediately. The correct starting point for most FlutterFlow apps is to allow read and write only when the requesting user is authenticated and the document's userId field matches their auth UID. For admin-only collections, require a custom claim. After editing, click Publish. FlutterFlow does not manage Firestore Security Rules — you must edit them directly in the Firebase Console or via the Firebase CLI.
1rules_version = '2';2service cloud.firestore {3 match /databases/{database}/documents {45 // Users can only read/write their own profile6 match /users/{userId} {7 allow read, write: if request.auth != null && request.auth.uid == userId;8 }910 // Users can only access their own records in any collection11 match /{collection}/{docId} {12 allow read, write: if request.auth != null13 && resource.data.userId == request.auth.uid;14 allow create: if request.auth != null15 && request.resource.data.userId == request.auth.uid;16 }1718 // Admin-only collection — requires custom claim19 match /admin_settings/{docId} {20 allow read, write: if request.auth != null21 && request.auth.token.admin == true;22 }23 }24}Expected result: Firestore rules are published. Unauthenticated requests and cross-user data access are blocked.
Verify Encryption At Rest and In Transit
Verify Encryption At Rest and In Transit
Firebase automatically encrypts all Firestore data at rest using AES-256 and all data in transit using TLS 1.3. You do not need to enable these — they are on by default and cannot be disabled on standard Firebase projects. To verify: open Firebase Console, go to Project Settings, and check the Google Cloud project ID. In the Google Cloud Console under Security, confirm the project has not disabled default encryption. For sensitive fields like medical data or private notes, consider field-level encryption using a Cloud Function that encrypts values with a key stored in Google Secret Manager before writing to Firestore. This way, even a compromised Firestore rule cannot expose plaintext.
Expected result: You have confirmed default encryption is active. Sensitive applications have a field-level encryption plan.
Schedule Automated Firestore Backups to Cloud Storage
Schedule Automated Firestore Backups to Cloud Storage
In the Firebase Console, go to Firestore Database and click the Import/Export tab. For automated daily backups, open Google Cloud Console, navigate to Cloud Scheduler, and create a new job. Set the schedule to 0 2 * * * (daily at 2 AM), the target type to HTTP, and the URL to the Firestore export REST endpoint: https://firestore.googleapis.com/v1/projects/YOUR_PROJECT_ID/databases/(default):exportDocuments. Set the request body to include your Cloud Storage bucket URI as the outputUriPrefix. Grant the Cloud Scheduler service account the Cloud Datastore Import Export Admin role in IAM. Exports are stored as .overall_export_metadata files in your Cloud Storage bucket.
Expected result: Cloud Scheduler creates a daily Firestore export in your Cloud Storage bucket.
Apply Data Minimization — Only Store What You Need
Apply Data Minimization — Only Store What You Need
Open FlutterFlow's Firestore panel and review every field in every Document Type. Delete fields that your app never reads. Common over-collection mistakes in FlutterFlow apps include storing full address strings when only city is needed, saving raw phone numbers when only verification status matters, and keeping failed payment attempt details when only the final status is relevant. In FlutterFlow's Create/Update Document actions, uncheck any fields you are not intentionally setting — this prevents accidental writes of empty or default values. Add a Firestore Security Rule validation using request.resource.data.keys() to allowlist which fields can be written, rejecting unexpected fields.
Expected result: Each Firestore collection contains only the fields your app actively uses.
Use Stripe Tokens for Payments — Never Store Card Data
Use Stripe Tokens for Payments — Never Store Card Data
If your FlutterFlow app handles payments, never write card numbers, CVVs, or expiry dates to Firestore. This is not just a best practice — storing raw card data without PCI DSS certification is illegal in most jurisdictions. Instead, use Stripe's Payment Intent flow: the Stripe SDK on the client collects card details and exchanges them for a PaymentMethod ID (pm_xxx) which is safe to pass to your server. In FlutterFlow, call a Supabase Edge Function or Firebase Cloud Function with the PaymentMethod ID and customer ID. The Cloud Function calls Stripe's API server-side to confirm the payment. Store only the Stripe customer ID (cus_xxx) and subscription status in Firestore — never the raw payment method details.
Expected result: No raw payment data exists in Firestore. Payments go through Stripe's SDK and Cloud Function, with only Stripe IDs stored.
Complete working example
1rules_version = '2';2service cloud.firestore {3 match /databases/{database}/documents {45 // Helper function: is the request from a signed-in user?6 function isSignedIn() {7 return request.auth != null;8 }910 // Helper function: does the document belong to the requesting user?11 function isOwner(userId) {12 return request.auth.uid == userId;13 }1415 // Helper function: does the new document set userId to the requesting user?16 function setsOwnUserId() {17 return request.resource.data.userId == request.auth.uid;18 }1920 // User profiles — read/write own profile only21 match /users/{userId} {22 allow read: if isSignedIn() && isOwner(userId);23 allow write: if isSignedIn() && isOwner(userId);24 }2526 // General user-owned data pattern27 match /time_entries/{docId} {28 allow read: if isSignedIn() && isOwner(resource.data.userId);29 allow create: if isSignedIn() && setsOwnUserId();30 allow update: if isSignedIn() && isOwner(resource.data.userId)31 && isOwner(request.resource.data.userId);32 allow delete: if isSignedIn() && isOwner(resource.data.userId);33 }3435 match /orders/{docId} {36 allow read: if isSignedIn() && isOwner(resource.data.userId);37 allow create: if isSignedIn() && setsOwnUserId();38 allow update, delete: if false; // Orders are immutable after creation39 }4041 // Admin-only settings42 match /app_config/{docId} {43 allow read: if isSignedIn();44 allow write: if isSignedIn() && request.auth.token.admin == true;45 }4647 // Block all other documents by default48 match /{document=**} {49 allow read, write: if false;50 }51 }52}Common mistakes when securing Your FlutterFlow App's Data
Why it's a problem: Creating a "backup" by downloading Firestore data to a local JSON file and storing it in the browser's Downloads folder
How to avoid: Use Cloud Scheduler to trigger automated Firestore exports to a Cloud Storage bucket with versioning enabled. The exports are encrypted, versioned, and accessible only to your Google Cloud project.
Why it's a problem: Leaving Firestore Security Rules as allow read, write: if true after initial development
How to avoid: Before launching, replace open rules with owner-based rules. Use the Firebase Console Rules Playground to test every role scenario.
Why it's a problem: Storing the Firebase service account JSON key file inside the FlutterFlow project or in a public GitHub repository
How to avoid: Service account keys belong only in Cloud Functions environment variables or Google Secret Manager. Never include them in client-side code or check them into version control.
Best practices
- Always start Firestore Security Rules with a catch-all deny rule and explicitly allow only what is needed.
- Enable Firebase Authentication before writing any Firestore rules — rules that reference request.auth are meaningless if Auth is not required.
- Run the Firebase Console Rules Playground after every rules change to verify the rules behave as expected for authenticated and unauthenticated users.
- Use Google Secret Manager for any secrets used in Cloud Functions — never hardcode API keys or service account credentials.
- Enable Cloud Storage versioning on your backup bucket so you can restore a previous backup if the latest export is corrupted.
- Audit your Firestore collections quarterly and delete fields or documents your app no longer reads.
- For apps handling health or financial data, contact RapidDev for a security architecture review before launch to ensure compliance requirements are met.
- Enable Firebase App Check to block requests from unofficial clients attempting to access your Firestore database.
Still stuck?
Copy one of these prompts to get a personalized, step-by-step explanation.
I have a FlutterFlow app with Firestore. My current Security Rules are allow read, write: if true. Help me rewrite them so that users can only read and write documents where the userId field matches their Firebase Auth UID. I also have an admin_settings collection that should only be writable by users with an admin custom claim.
Review my Firestore Security Rules and identify any rules that allow unauthenticated access or cross-user data access. Suggest a rewrite using helper functions for isSignedIn and isOwner patterns.
Frequently asked questions
Does Firebase automatically encrypt my Firestore data?
Yes. Firebase encrypts all Firestore data at rest using AES-256 and all data in transit using TLS 1.3 by default. You do not need to configure anything. This cannot be disabled on standard Firebase projects.
How do I know if my Firestore Security Rules are currently open to the public?
Open Firebase Console, go to Firestore Database, click the Rules tab. If you see allow read, write: if true anywhere, your database is public. Also check the Firebase Console dashboard — it shows a security warning banner if your rules are open.
Can users see each other's data if they inspect the network traffic in my FlutterFlow app?
If your Firestore Security Rules are correct, no. Even if a user intercepts the network request and replaces the document ID with another user's ID, Firestore will reject the request because the rule checks request.auth.uid against the document's userId field server-side.
What is the difference between Firestore Security Rules and FlutterFlow's query filters?
FlutterFlow's query filters (where userId equals currentUser.uid) are a UI convenience that makes queries efficient and correct. They do not prevent a malicious user from bypassing your app and calling the Firestore API directly. Firestore Security Rules are enforced server-side and cannot be bypassed — they are your real security layer.
Do I need to pay for Firestore backups?
Firestore export operations are free, but you pay for the Cloud Storage used to store the export files. For most small apps, a daily export is a few megabytes and costs less than $0.05 per month.
Can I restore my app from a Firestore backup if something goes wrong?
Yes. Go to Firebase Console, Firestore Database, Import/Export tab, and click Import. Select the .overall_export_metadata file from your Cloud Storage bucket. Note that imports merge with existing data — they do not overwrite. For a clean restore, you may need to delete the existing collection first.
Talk to an Expert
Our team has built 600+ apps. Get personalized help with your project.
Book a free consultation