Guide · Mobile app security

Can a mobile appkeep a secret?

A mobile app runs on a device you do not control. Its package can be unpacked, its network traffic can be observed and its behaviour can be changed. That does not make every value found inside it a vulnerability—but it does mean a real server secret cannot live there. This guide explains the boundary, using Firebase as the case people most often misunderstand.

The short answer: assume the client can be inspected

When an Android or iOS app is released, every user receives a copy of its executable code and resources. The original source is not handed over neatly, but common tools can recover strings, configuration files, endpoints and a useful approximation of the program's structure. An attacker does not need to reconstruct the whole app to search for a token or replay one request.

Obfuscation is still useful: it removes friendly names and raises the cost of understanding the code. It is a speed bump, not a vault. A value the app must read can eventually be read by someone controlling the device. The durable rule is therefore simple: design the client as observable and keep authority on a system you control.

Reasonable in the appKeep on the server
ConfigurationAPI base URL, project ID, feature availabilityPrivate network locations and administrative configuration
IdentityA short-lived token for the signed-in userService-account keys and signing secrets
Third-party servicesA deliberately public, tightly restricted client keyPayment, email, AI-model and unrestricted API keys
Business rulesValidation that improves the interfaceAuthorization, prices, entitlements and fraud decisions
A useful boundary: configuration identifies the app; server credentials authorize privileged actions.
A phone connected to a laptop beside an inspected application package represented by unlabelled printouts and a metal key.
The installed package is available to inspect; privileged credentials should not be inside it.

What decompiling an app can actually reveal

A first pass is often mundane. Unpack the application, list its resources and search the strings. That can reveal server addresses, analytics identifiers, Firebase configuration, feature flags and credentials that were copied into a build file. The next step is observing the requests the app makes. If a privileged action depends only on a value embedded in the client, the problem is not that the decompiler was clever; the trust boundary was in the wrong place.

Runtime storage needs the same care. A token issued to a signed-in user has to reach the device, so the goal is not to make it invisible forever. Give it a narrow scope and short lifetime, store it with the platform's protected storage, transmit it over TLS and let the server verify what that user may do on every request.

Do not confuse inconvenience with protection. Splitting a key across strings, encoding it or downloading it after launch may hide it from a basic search, but the complete value must exist when it is used. These techniques can slow bulk scraping; they cannot turn a shared secret into a private one.

The Firebase API key exception—and what it does not excuse

Open a Firebase Android build and you will usually find an API key in google-services.json; an Apple app carries the equivalent configuration. This alarms many audits, but Firebase documents these keys as public by design when they are restricted to Firebase-related APIs. They identify the project. They do not authorize a user to read Firestore, Realtime Database or Cloud Storage.

The locks are Firebase Security Rules, backed by the user's authentication state. App Check adds another signal: it helps a supported backend reject traffic that did not come from an attested instance of your app. Neither control is replaced by hiding the Firebase key.

The nuance matters. The same Google Cloud project can contain other keys whose services do charge or authorize by key. A key usable with a mapping, payment, email or generative-AI API may be valuable to an attacker. Use separate keys, restrict each one to the expected API and application, set sensible quotas, and keep any service that requires a true secret behind your server.

The key is limited to Firebase services
Review Security Rules, Authentication and App Check.
The key also enables another Google API
Separate it and apply API and application restrictions.
The app contains a service account or private key
Revoke it, investigate its use and move the operation server-side.
Data is readable without the intended user rights
Treat the rules or backend authorization as the vulnerability.
Finding a Firebase API key starts a configuration review; it does not, by itself, prove a data leak.

Put the security decision on the server

Imagine a booking app that shows available appointments and lets a customer reserve one. The app may know the public API address and carry a token for the signed-in customer. It should not decide that an appointment is free, set its own price or call the payment provider with your private key.

Instead, the server receives the request, verifies the user, checks the current availability and price, performs the privileged action with a server-side credential and returns only the result. This example is fictional, but the boundary is the same in a customer portal, a warehouse app or a subscription product.

Client-side checks still improve the experience: disable an invalid button, catch a malformed date, explain why an action is unavailable. Repeat every security-relevant check on the server, where a modified app cannot simply skip it.

  1. 01 App requests User intent plus a short-lived identity token
  2. 02 Server verifies Identity, permission, current state and limits
  3. 03 Service acts The private credential never reaches the phone
  4. 04 Result returns Only the data the user is allowed to receive
The app asks; the server authenticates, authorizes and performs the privileged action.

What App Check, Play Integrity and App Attest add

Authorization answers whether this user may perform this action. App integrity answers a different question: does the request appear to come from a genuine, untampered instance of the app? Firebase App Check can use Play Integrity on Android and App Attest or DeviceCheck on Apple platforms to provide that signal to Firebase services or your own backend.

That is valuable for limiting automated abuse, copied clients and some modified-app traffic. It is not a magic proof. Devices can be unavailable or compromised, attestation services can fail, and a genuine app can still be driven by a malicious user. Roll enforcement out while monitoring, define what happens when attestation is unavailable and keep ordinary authentication, authorization, rate limits and logging in place.

Use stronger checks at valuable moments rather than turning every harmless screen into a fortress: claiming a promotion, creating an expensive AI request, downloading paid content or changing a payment destination are better candidates than reading a public catalogue.

A practical release review

Before release, inspect the same artefact users will install—not only the source repository. Search the built package for credentials and private material; list every outbound service; verify that each key is public by design or restricted to the minimum; and attempt important requests without the app's own interface.

Then test authorization with two ordinary accounts. Can one read or change the other's records by editing an identifier? Can either invoke an administrative endpoint? Are Firebase rules tested against denied cases as well as happy paths? Finally, check logs, quotas and alerts: a control you cannot observe is difficult to trust in production.

  • No service-account files, signing secrets, payment secrets or private keys in the package.
  • Every client key has an owner, a purpose, API restrictions and a rotation plan.
  • The server rechecks identity, ownership, roles, prices and entitlements.
  • Firebase Rules deny by default and have emulator tests for allowed and denied requests.
  • App attestation complements—never replaces—authentication and authorization.
  • Rate limits, budgets and alerts make abuse visible before the invoice does.

If you find a real secret in a released app

Treat the value as exposed. Revoke or rotate it first, using the provider's safe overlap procedure if an immediate cutover would break production. Review access logs, billing and changes made with the credential; preserve enough evidence to understand the window; and tell the relevant people if data or accounts may have been affected.

The permanent fix is architectural: put the privileged call behind a server or managed function, authorize the user there and give that runtime only the permission it needs. Shipping another app version with the same secret encoded more creatively only restarts the clock.

Sources and further reading

Questions before we start

Can someone really decompile an Android or iOS app?

They can inspect the package, recover resources and strings, approximate parts of the code and observe runtime behaviour. Obfuscation raises the effort but does not make a value used by the app permanently secret.

Is exposing a Firebase API key a security vulnerability?

Usually not by itself. Firebase client keys are designed to identify the project. Check that the key is restricted to the intended Firebase APIs, then test Security Rules, Authentication and App Check—the controls that actually protect the data and backend.

Should I hide an API key with obfuscation or encryption?

Only as an extra obstacle for a key that is already safe to distribute. If every copy of the app can decrypt a shared credential, a determined analyst can recover it. A true server secret belongs outside the app.

Does App Check stop every copied or modified client?

No. It adds a useful app-integrity signal and reduces several forms of abuse, but it needs monitoring and fallbacks and does not replace user authentication, server-side authorization, quotas or logs.

LET'S TALK

Let’s discuss your project.

A LITTLE HELP GETTING STARTED

A clearer idea. A better first meeting.

Tell me what you want to build or improve. I’ll help you put together a short brief for Artur.

Describe my idea
Project assistant

Project assistant

ARTUR PUIG

Which app flow are you concerned about?

Start with a sentence or two. I’ll help you turn it into a useful conversation with Artur.

By sending, you confirm you are 18+ and agree to Google processing your idea. Artur keeps the chat for 14 days and receives a summary via Telegram. Do not include sensitive data. Google ↗

A FREE FIRST CONVERSATION

Book a free consultation.

A 30-minute video call to discuss your idea or a problem you want to solve. If a basic demo would help, we can agree what it should show.

Free · 30 min · Google Meet

Book a free consultation

ARTUR PUIG · Free · 30 min · Google Meet

Let’s find a time.

Let’s talk about your project

Write to me here.

Prefer to write? Send a brief summary and I’ll reply by email.

Your message comes straight to me. No newsletter, no sales calls.

Add details (optional)

I’ll use your details to respond to your enquiry. The form uses Cloudflare spam protection and delivers your message to me through Telegram.

Connect on LinkedIn

Based in Barcelona · Working remotely