The Outcome, First
Quittr was doing $1M in 10 days. Then anyone on the internet could read every word its users had ever typed.
That is the story. A vibe-coded app, built at speed, launched to enormous early traction, with Firebase Realtime Database rules configured as ".read": true, ".write": true — the default permissive setting from development, shipped unchanged into production. The database had no access controls. Zero. Anyone with the database URL could read the entire dataset without authentication.
Approximately 39,000 users were affected. These were not generic email addresses in a marketing list. These were people sharing the most vulnerable moments of their lives: their day counts, their relapses, their private messages to accountability partners, their daily journals about fighting addiction. The kind of data people would not share with their closest friends, let alone the public internet.
The technical fix was three lines of JSON. The breach itself was, by any reasonable assessment, preventable in under five minutes. What follows is a full technical postmortem — what happened, why it happens so often, how to check for it in your own app, and what a structured scan would have caught before this went live.
What Quittr Was
Quittr is an accountability app built for people quitting addictions — alcohol, substances, social media, pornography, gambling. The core product is simple: users log their sobriety, journal about their experience, connect with an accountability partner, and check in daily. That simplicity is why it resonated. It hit $1M in revenue within 10 days of launch, driven almost entirely by organic sharing — people recommending it to friends who were struggling.
The viral loop was powered by vulnerability. Users were not sharing screenshots of a leaderboard or a fitness PR. They were sharing genuine, raw personal disclosures about addiction. The app's growth came directly from its users feeling safe enough to open up inside it.
That context is what makes this breach categorically different from a leaked email address or a credit card number. Financial data is damaging, but it is recoverable — you cancel the card, you file a dispute. Sobriety data is not recoverable. Someone's day count at the time of a relapse, the exact words they wrote to their accountability partner at 2am, the specific addiction they were fighting — that data, once exposed, cannot be unexposed.
Health-adjacent behavioral data — addiction journals, mental health disclosures, sobriety records — is classified as sensitive personal information under GDPR Article 9 and CCPA. A breach of this data carries legal exposure beyond a standard data incident, and the reputational damage to users is not bounded by financial remediation.
The app was vibe-coded — built rapidly using AI coding assistants, likely with Firebase chosen for its easy real-time data sync and fast setup. Nothing wrong with that. The tools are not the problem. The gap is that AI coding tools provision the database but do not automatically configure security rules for production. That is a step that requires explicit human intention.
What "Public" Firebase Rules Actually Mean
Firebase Realtime Database uses a JSON-based rules system to control who can read and write data. These rules are separate from your authentication setup. Even if your app requires users to log in through Firebase Auth, the database itself has independent access controls that must be configured explicitly.
When you create a new Firebase project and select "test mode" — which is the default for most quick setups — the database ships with these rules:
{ "rules": { ".read": true, ".write": true } }
What does ".read": true mean in practice? It means anyone — authenticated or not, your user or a stranger, a person or a script — can send an unauthenticated GET request to your Firebase database URL and receive your entire dataset as JSON. Not a subset. Not a paginated view. The whole thing.
The URL format is predictable: https://[your-project-id].firebaseio.com/.json
That endpoint, if your rules are permissive, returns your complete database tree as a single JSON object. No API key. No login. Just a browser, or curl, or a Python script in a loop. The check takes less than 60 seconds to run manually.
When you build with an AI coding assistant, the model generates the Firebase integration code — the SDK initialization, the read and write functions, the auth flow. It does not generate your production security rules, because rules live in a separate Firebase console configuration, not in your codebase. The code works locally with permissive rules. The AI has no way to know you shipped those same rules to production.
Firebase does display a banner in its console warning that your rules are public. That warning is easy to overlook when you are shipping fast, context-switching between the console and your IDE, and focused on feature completion rather than configuration hardening.
The vulnerability is not obscure. It is the first thing any security researcher checks on a Firebase-backed app. It has been the root cause of dozens of documented breaches over the past several years, across apps in health, finance, productivity, and consumer social. Quittr is not an outlier — it is a representative case in a well-documented pattern.
What Was Actually Exposed
The Quittr Firebase database, accessible at https://quittr-[id].firebaseio.com/.json, returned the full database tree. A request to the root returned every user's data in a single response. No pagination, no authentication, no rate limiting at the data layer.
The structure exposed included:
User records: Firebase user IDs (UIDs), registration timestamps, profile display names, addiction type selected on signup, daily check-in streak counters.
Sobriety journals: Full text of every journal entry, timestamped. Entries ranged from brief check-ins to multi-paragraph disclosures about relapses, triggers, and emotional state.
Accountability partner data: Partner UID mappings, private message threads between users and their partners, message content and timestamps.
Progress tracking: Day counts, milestone records, relapse logs, goal statements entered during onboarding.
Access required no special knowledge. A security researcher, a competitor, a journalist, a former partner, or anyone with a grudge could have run a single curl command and downloaded the complete dataset for all 39,000 users. The export would have taken seconds on a standard connection.
There is no way to determine after the fact whether the data was accessed before the rules were corrected. Firebase's default logging does not capture unauthenticated read access in a way that distinguishes legitimate versus malicious requests. If someone downloaded the database, there is no forensic record of it. That is the permanence problem with this class of breach: the window of exposure, and what happened inside it, cannot be fully audited.
CVSS v3 Scoring
Using the Common Vulnerability Scoring System version 3.1, this finding scores as follows:
| AV:N — Attack Vector: Network | Exploitable remotely over the internet. No local or physical access required. |
| AC:L — Attack Complexity: Low | No special conditions, no race conditions, no prior knowledge required. One HTTP request. |
| PR:N — Privileges Required: None | No account, no API key, no authentication token of any kind. The endpoint is fully public. |
| UI:N — User Interaction: None | The attacker does not need the victim to take any action. No phishing, no social engineering. |
| S:U — Scope: Unchanged | The vulnerability does not allow the attacker to pivot to other systems. |
| C:H — Confidentiality: High | Complete disclosure of all user data. The attacker gains access to the entire database. |
| I:N — Integrity: None | Write rules were also public, but the primary measured impact is on confidentiality. |
| A:N — Availability: None | The service continues to operate during the exposure. No denial of service effect. |
A CVSS score of 7.5 (High) reflects a finding that is trivial to exploit, requires no attacker resources, and results in complete confidentiality loss across all user data. In practice, the sensitivity of the data — health-adjacent behavioral disclosures — makes the real-world impact higher than the score alone suggests.
The Three-Line Fix
The remediation required no architectural changes, no new dependencies, no deployment pipeline. It was a configuration update in the Firebase console — three lines of JSON changed, rules published, breach closed.
The condition auth.uid !== null requires a valid Firebase authentication token to be present in the request. An unauthenticated request — the kind that made the full database publicly accessible — now returns a 401 Permission Denied response.
This is the minimum viable fix, not the recommended production posture. For a production app with user-specific data, the correct implementation adds per-user path rules so that a logged-in user can only access their own data:
{ "rules": { "users": { "$uid": { // Users can only read and write their own data ".read": "$uid === auth.uid", ".write": "$uid === auth.uid" } } } }
The auth-required minimum would have prevented the public breach. The per-user path rules prevent a compromised user account from reading other users' data — a separate risk class that should be addressed in any production deployment handling sensitive personal data.
How Launch Ready Code Would Have Caught This
This finding is detectable with a URL-based scan in under two minutes. No code access required. No repository credentials. Just the app's live URL.
Our scanner probes known Firebase endpoint patterns as part of the standard monitoring dimension check. When a Firebase project is detected from the app's JavaScript bundle or network requests, the scanner constructs the /.json probe and evaluates the response code and body.
A 200 response with JSON content at the root endpoint is an automatic P0 flag. Here is the exact finding our system surfaces:
https://[app].firebaseio.com/.json returns full database content (HTTP 200)
".read": "auth.uid !== null"
This probe runs as part of every free scan at launchreadycode.com. It does not require you to share your codebase. It runs entirely against the live, public-facing URL of your application. The scan covers Firebase Realtime Database, Firebase Firestore (via REST API probe), and common cloud storage misconfiguration patterns in the same pass.
For a Quittr-scale app — live, public, handling sensitive user data — the scan completes in under two minutes and would have surfaced this finding before the first paying user signed up.
Enter your app URL. We probe for public database access, unauthenticated API endpoints, exposed secrets, and 47 other critical security checks. No code access required. Results in under two minutes.
Run free scan nowWhat This Means for Vibe Coders
Quittr is not a careless team. It is a fast team. There is a meaningful difference. The developers shipped a product that tens of thousands of people found valuable enough to pay for within 10 days of launch. That is real engineering and product work, done at speed. The Firebase rules gap is not a sign of incompetence — it is a predictable failure mode of the build-fast approach when it is not paired with a structured security check at launch.
The systemic issue is this: Firebase's default development rules are permissive by design. They exist to let you build without friction. The assumption is that you will update them before going live. Many teams do not, because the transition from "development" to "production" is often not a single discrete moment — it is a gradual process of adding users, and the rules update is easy to defer when everything appears to be working.
Firebase is not alone in this pattern. Supabase, a popular alternative used heavily in vibe-coded projects, ships with Row Level Security (RLS) disabled by default on newly created tables. A Supabase table without RLS enabled is functionally equivalent to a Firebase database with ".read": true — any authenticated user can read any row in any table. We have built a dedicated checker for this pattern: run the Supabase RLS checker here.
The lesson is not "do not use Firebase" or "do not build fast." The lesson is that database-layer security is not something your AI coding tool can verify for you. It is a configuration step that exists outside your codebase, in a console, and it requires explicit human attention before you take your first real user. Build fast. Ship often. But add one checkpoint: a URL-based security scan before you open signups. It takes two minutes. The cost of skipping it, as Quittr demonstrates, is measured in something that cannot be refunded.
See our full pricing for ongoing monitoring that catches new configuration drift as your app evolves: launchreadycode.com/pricing.
Share This
If you know a founder shipping fast with Firebase or Supabase, this is worth sending them. Copy the post below:
Frequently Asked Questions
".read": true and ".write": true — the default test-mode setting, shipped unchanged to production. Anyone with the database URL could read all user data — sobriety journals, private messages, accountability partner connections, personal progress records — without any authentication. Approximately 39,000 users were affected.
".read": true, ".write": true) in test mode, which is the default for most quick setups. These rules must be explicitly changed before going live. Firebase shows a warning in its console, but this is easy to miss when moving fast. Supabase has an equivalent issue: Row Level Security (RLS) is disabled by default on new tables. Both platforms assume the developer will configure security rules before production — many do not.
https://YOUR-PROJECT-ID.firebaseio.com/.json. Replace YOUR-PROJECT-ID with your Firebase project ID (visible in your Firebase console under Project Settings). If the request returns JSON data rather than a permission error, your database is publicly readable. This check takes under 60 seconds and requires no special tools. Alternatively, run a free scan at launchreadycode.com — we probe this endpoint automatically as part of every scan.
{ "rules": { ".read": "auth.uid !== null", ".write": "auth.uid !== null" } }. This requires users to be authenticated before reading or writing any data. For a production app, add per-user path rules so users can only access their own data: { "rules": { "users": { "$uid": { ".read": "$uid === auth.uid", ".write": "$uid === auth.uid" } } } }. Publish the updated rules immediately — the change takes effect within seconds.