You push a quick fix on Friday, open your repo in a browser, and realise a token that should've stayed private is now sitting in a commit diff. That moment captures why sensitive data exposure keeps catching teams off guard. It rarely starts with a dramatic breach. It starts with ordinary development behaviour: a rushed deploy, a permissive database rule, a mobile app bundle that includes more than it should.
Modern stacks make this easier to do by accident. Supabase gives you direct database access patterns. Firebase makes it simple to ship quickly. Mobile apps need keys, config, and storage access. Each of those conveniences can widen the blast radius when access rules are loose or secrets end up in the client.
This isn't a niche problem. In the UK, the government's Cyber Security Breaches Survey 2025 estimated that 43% of businesses and 30% of charities experienced at least one cyber security breach or attack in the previous 12 months, affecting about 612,000 businesses and 61,000 charities. For startup teams, the practical takeaway is simple: assume accidental exposure is a routine engineering risk, not an edge case.
The Hidden Risk in Your Codebase
Sensitive data exposure usually doesn't look like a Hollywood attack. It looks like normal product work. A developer leaves debug logging enabled. A Firestore rule gets relaxed during testing and never tightened. A Supabase policy allows any authenticated user to read rows that should be private. The app still works, so nobody notices.
That's why teams miss it. Most exposure bugs don't break functionality. They preserve it. The app loads faster, the query succeeds, the integration works, and private data becomes visible to users who were never meant to see it.
Why modern apps are especially exposed
BaaS platforms remove a lot of backend boilerplate, which is good for speed. They also put more security decisions closer to frontend and product teams. That changes where mistakes happen.
A few patterns show up repeatedly:
- Client-visible configuration: Public bundles and mobile binaries often reveal endpoints, project identifiers, and sometimes secrets that were never meant to ship.
- Over-broad reads: Teams fetch full records when the UI only needs a name or avatar.
- Temporary rules that become permanent: Test settings survive into production because nobody wants to risk breaking onboarding or billing flows.
Practical rule: If a misconfiguration makes development easier, assume it may also make data exposure easier.
What makes these bugs expensive
Exposure isn't only about theft. It's also about unnecessary visibility. If support notes, invoices, auth tokens, or internal IDs become broadly readable, you now have a security issue and an operational issue. You need to figure out what was exposed, who could access it, and whether your logs are detailed enough to answer that question.
Startups often over-focus on keeping attackers out and under-focus on reducing what any given component can see. That's the wrong trade-off. In practice, reducing read access is usually faster and more durable than trying to harden every edge perfectly.
What Exactly Is Sensitive Data Exposure
Sensitive data exposure means information is stored, transmitted, or returned in a way that lets the wrong party access it. The simplest analogy is a house key under the doormat. The key still “works”, but anyone who knows where to look can use it. In app terms, the data may still be inside your product boundary, yet effectively exposed because access controls, storage rules, or transport protections are weak.

What counts as sensitive data
Developers often think only of passwords or card details. The actual list is much broader.
- Personal data: A name alone may be harmless in one context, but a name paired with postcode, date of birth, phone number, or account history becomes much more sensitive.
- Authentication material: Password hashes, session tokens, refresh tokens, API tokens, OAuth secrets, magic link tokens.
- Financial records: Billing addresses, invoice PDFs, payout details, transaction histories.
- Health or special category information: Medical notes, support disclosures, accessibility details, anything that creates a high impact if exposed.
- Business-sensitive data: Internal pricing logic, source code, unpublished roadmap items, vendor credentials.
If you're building a new inventory for your app, this list of discover sensitive data types is a useful prompt for spotting fields that teams tend to underestimate.
Exposure versus encryption failure
Not all exposure bugs mean the data was dumped publicly on the open web. Sometimes the flaw is subtler. A mobile app requests a profile object and gets email, role, billing status, and internal notes, even though the screen only displays a username. The transport might be encrypted, but the API still exposed more than necessary.
That distinction matters. Sensitive data exposure often comes from bad scoping, not just missing cryptography.
Data that is technically protected in one layer can still be overexposed in another.
A useful classification test
When deciding whether a field should be treated as sensitive, ask three questions:
- Would a user expect this to remain private?
- Would exposure create harm for the user or the business?
- Does the UI or workflow require broad access to this field?
If the answer to the third question is no, don't return it to the client. That one habit prevents a surprising number of problems.
Common Attack Vectors and Real World Examples
Most sensitive data exposure in modern apps comes from ordinary implementation shortcuts. The vulnerability is usually plain once you see it. The hard part is that nobody sees it during a normal happy-path test.

Leaky APIs that return too much
A classic example is a profile endpoint that returns the whole user record because it was convenient during development:
{
"id": "user_123",
"display_name": "Sam",
"email": "sam@example.com",
"role": "admin",
"billing_status": "active",
"internal_notes": "chargeback review",
"phone": "..."
}
The frontend only needs display_name. Everything else is now exposed to the browser, mobile client, and any authenticated user who can call that endpoint. This is common in GraphQL resolvers, autogenerated REST endpoints, and direct table access patterns.
Supabase policies that look safe but aren't
A startup adds Row Level Security, sees the toggle enabled, and assumes the table is protected. Then the policy says “authenticated users can select”, which means every signed-in user can read every row.
Bad policy:
create policy "profiles are readable by authenticated users"
on public.profiles
for select
to authenticated
using (true);
That policy doesn't scope anything. If profiles includes email addresses, phone numbers, or account metadata, you've created a data exposure path.
Firebase rules that drift during testing
This happens when a team loosens rules to get a launch blocker out of the way:
match /users/{userId} {
allow read, write: if request.auth != null;
}
It feels temporary. It often isn't. Now any authenticated user can read every user document if they know or can enumerate IDs. The same pattern shows up in Storage rules, where uploaded invoices, profile exports, or support attachments become too broadly readable.
Secrets in frontend and mobile code
Developers still confuse “not shown in the UI” with “not accessible”. If a secret is in a React bundle, APK, or IPA, it's client-side material. It can be extracted.
A common sequence looks like this:
- Step one: A third-party API key is hardcoded because the feature has to ship.
- Step two: The app works in staging and production.
- Step three: Someone inspects the bundle or decompiles the app and reuses the credential.
If you want a concrete example of that class of issue, this breakdown of API key exposure risks maps closely to what teams run into with web and mobile builds.
A secret in client code isn't a secret. It's configuration you've published.
How to Detect Data Exposure Vulnerabilities
Detection works best when you combine three different views of the system: fast scanning, exploit-style verification, and runtime visibility. Relying on only one of them leaves blind spots.

Automated scanning for obvious mistakes
Automated scanners are good at finding the things humans skip over. That includes public storage buckets, exposed RPC functions, weak Firebase rules, leaked keys in frontend assets, and broad Supabase policies. For teams shipping frequently, this is the fastest way to catch regressions before a release becomes a cleanup exercise.
AuditYour.App is one option in this category. It scans Supabase, Firebase, websites, and mobile binaries for exposed RLS rules, leaked keys, unprotected functions, and related misconfigurations. The value isn't just speed. It's that the tool checks the stack developers are using, instead of assuming a traditional server-rendered backend.
Logic fuzzing to prove real leakage
Static checks can tell you a rule looks dangerous. They can't always prove whether data is reachable. That's where logic fuzzing helps.
Take a Supabase table with multiple policies, joins, and helper functions. A static linter might flag the table as “potentially exposed”, but that still leaves room for doubt. Fuzzing behaves more like an attacker. It tries reads and writes across identities and payloads to see what really leaks.
That matters for noisy environments. Teams often ignore generic warnings because they get too many of them. When a test proves user A can read user B's row, the discussion changes immediately.
Instrumentation and access monitoring
The third layer is observability. You need logs that answer practical questions:
- Who accessed which records
- Which API path returned the data
- Whether access came from an expected client
- Whether read volume or query shape changed suddenly
This is also where many startups are weakest. They log errors, but not data access. So when they discover an exposure bug, they can patch it but can't assess impact confidently.
A good logging setup doesn't need to be huge. It needs to be intentional. Record sensitive reads at service boundaries, watch for unexpected document scans, and review unusual patterns that could indicate data harvesting. This complements broader guidance around data exfiltration prevention, especially when the risk isn't one loud breach but quiet repeated access.
Detection without proof creates debate. Detection with proof creates a fix.
Prioritised Remediation for Modern Apps
The most effective remediation pattern is still the boring one: classify data, then enforce least privilege around it. OWASP recommends classifying sensitive data and applying controls accordingly, while UK ICO guidance emphasises keeping personal data secure and limiting access to what is necessary, which is why classification plus least-privilege access is the most actionable control for UK-facing teams working on sensitive data exposure (OWASP guidance on sensitive data exposure).
Start with the data, not the framework
Don't begin with “how do we secure Firebase” or “what's the right Supabase policy”. Begin with a smaller question: which fields would hurt if the wrong user could read them?
Once you've tagged those fields, apply controls in this order:
- Reduce collection if the app doesn't need the field.
- Reduce retention if the business no longer needs it.
- Reduce visibility so only the right role or owner can read it.
- Reduce propagation so APIs and clients only receive the minimum subset.
Supabase RLS bad versus good
The bad version is broad enough to be dangerous:
create policy "authenticated users can read profiles"
on public.profiles
for select
to authenticated
using (true);
A safer owner-scoped version ties access to the signed-in user:
alter table public.profiles enable row level security;
drop policy if exists "authenticated users can read profiles" on public.profiles;
create policy "users can read their own profile"
on public.profiles
for select
to authenticated
using (auth.uid() = user_id);
create policy "users can update their own profile"
on public.profiles
for update
to authenticated
using (auth.uid() = user_id)
with check (auth.uid() = user_id);
If the table contains admin-only fields, split them out into a separate table instead of trying to hide a few columns inside the same broad record. That's easier to reason about and easier to audit.
Firebase rules bad versus good
Over-broad Firestore rule:
match /users/{userId} {
allow read, write: if request.auth != null;
}
Scoped rule:
match /users/{userId} {
allow read, update, delete: if request.auth != null
&& request.auth.uid == userId;
allow create: if request.auth != null
&& request.auth.uid == userId;
}
For nested records such as invoices or private attachments, don't inherit broad user-level access unless that is explicitly intended. Write separate rules for each collection based on ownership or role.
Client and mobile secret handling
Environment variables don't magically make secrets safe. They only help if the value is injected into a server-side runtime or build process and never shipped to the client unless it's meant to be public.
Use this mental split:
- Public config: Project URL, publishable key, analytics identifier.
- Private secret: Service role key, third-party admin token, signing secret.
For mobile apps, assume anything bundled into the app can be recovered. Put privileged actions behind a server or edge function, then authenticate the caller and execute the secret-bearing operation there.
If your team needs a cleaner operating model for preventing credential leaks, centralised secrets management is worth implementing early. It removes the habit of passing secrets around in .env files, chat threads, and local notes.
Remediation priority checklist
| Priority | Vulnerability | Example Fix | |---|---|---| | Highest | Broad read access to user records | Restrict reads to record owner or required role | | High | Public or weakly protected storage objects | Require authenticated access and path-based ownership checks | | High | Secrets embedded in client or mobile code | Move secret use to a server-side function | | Medium | APIs returning full objects | Return only required fields with explicit selects | | Medium | Old data retained indefinitely | Delete or archive records that no longer serve a business purpose |
Building a Secure Development Lifecycle
Reactive clean-up is expensive because the hardest part isn't usually patching the bug. It's reconstructing exposure after the fact. If you don't know which systems held the data, which client requested it, or which rule changed, you'll spend more time answering basic questions than fixing the root cause.

The controls that keep paying off
For teams building on BaaS platforms, a secure lifecycle needs a few key requirements:
- Security requirements at design time: Decide which tables, collections, and storage paths contain sensitive fields before the schema hardens.
- Threat modelling around access paths: Review how web, mobile, admin tooling, and background jobs read the same records.
- Security checks in CI: Fail builds when policies, rules, or bundled assets introduce obvious exposure paths.
- Change review for data access logic: Treat RLS, Firestore rules, and edge function auth checks as critical code, not plumbing.
A useful reference point for engineering managers is this overview of the secure software development life cycle, especially where release processes need stronger security gates.
Encryption and key separation
Encryption matters, but teams often stop at “turn it on” and assume the job is finished. What matters operationally is whether sensitive data is protected in transit and at rest, and whether keys are managed separately with tight access controls. If the same system and same broad role can reach both data and keys, you've reduced the protection value.
This is especially relevant for startup stacks that combine managed databases, object storage, and mobile clients. The easier your platform makes connectivity, the more disciplined you need to be about separation of duties.
Build so that one mistake exposes as little as possible.
What doesn't work
A few habits keep failing teams:
- Security reviews only before launch: Exposure bugs are often introduced after launch in routine iteration.
- One shared admin credential for convenience: This destroys accountability and expands blast radius.
- Assuming platform defaults are enough: Supabase and Firebase provide strong primitives. They don't know your intended data boundaries.
- Treating logs as optional: If you can't trace access, you're guessing after an incident.
The teams that stay out of trouble don't rely on heroics. They make insecure changes harder to merge and easier to detect.
Compliance and Incident Response Planning
For UK organisations handling personal data, encryption and strong key management reduce the impact of exposure, but they don't remove the need for visibility. Guidance also notes that exposures can persist undetected for long periods, which is why continuous monitoring, logging of data access, and periodic configuration audits are justified controls for catching open databases, permissive APIs, or leaked secrets (overview of sensitive data exposure and monitoring needs).
From a compliance angle, that maps directly to practical expectations under UK GDPR. If you collect personal data, you need to show that access is controlled, that sensitive data isn't unnecessarily exposed across systems, and that you can investigate when something goes wrong. Logging isn't just an engineering preference. It's part of proving due care.
A simple incident response flow
When a data exposure issue appears, teams need a short path from discovery to action:
- Containment: Revoke the leaked key, disable the affected rule, lock down the bucket, or roll back the release.
- Assessment: Identify which data types were exposed and which systems or users could access them.
- Notification: Work with legal and operations to decide whether notification obligations apply.
- Review: Fix the root cause, then change the process that allowed it through.
Staff training matters here too, because exposure often starts with ordinary mistakes rather than advanced intrusion. If your organisation is formalising response readiness, this guide to compliant GDPR staff programs is a useful operational reference.
The biggest mistake after containment is treating the incident as closed. If you don't update schema design, access review, secret handling, or release checks, the same class of bug will come back.
If you want a fast way to check whether your Supabase, Firebase, web, or mobile app is leaking data through weak rules, exposed functions, public storage, or bundled secrets, AuditYour.App gives you a practical place to start. It's designed for modern app stacks and focuses on the exact misconfigurations that tend to cause sensitive data exposure in real products.
Scan your app for this vulnerability
AuditYourApp automatically detects security misconfigurations in Supabase and Firebase projects. Get actionable remediation in minutes.
Run Free Scan