2026-09-28 · 9 min read

    The API Key You Forgot Was Still in Your Codebase

    An API key can look harmless in a Flutter project until you realize that anything shipped inside a mobile app should be treated as potentially discoverable. Here's why credential handling matters.

    Flutter securitymobile app securityAPI key securitycredential securityFluttermobile engineering

    There's a particular kind of security problem that doesn't feel like a security problem when you create it.

    You add an API key to a configuration file because the SDK needs it. You put a token in a constant because the app needs to authenticate with a service. You add a credential to an environment file because that's what everyone says you should do. The app works. You commit the code. CI builds successfully. You move on to the next feature.

    Months later, someone discovers the key sitting in the repository. Or worse, they discover it somewhere else first.

    The uncomfortable part is that nothing necessarily went wrong at the moment you added it. The code did exactly what you asked it to do. The problem was that the credential was treated like ordinary configuration when it was actually a security-sensitive piece of information.

    This is the problem behind improper credential usage.

    And in mobile development, there is one assumption worth challenging early: if a secret has to be shipped inside the application, you should not assume it will remain secret.


    What "Improper Credential Usage" Actually Means

    Credentials are pieces of information that allow something to prove its identity or gain access to a resource.

    That can mean API keys, access tokens, client secrets, signing credentials, passwords, private keys, service credentials, or other authentication material.

    The mistake isn't always putting a credential directly into a Dart file.

    It can be using a credential with more permissions than the application actually needs. It can be storing a long-lived token where a short-lived token would have been safer. It can be committing secrets to source control and assuming that deleting the line later makes the credential disappear. It can be using the same credential across development, staging, and production because keeping three sets of credentials feels inconvenient.

    The common thread is treating credentials as if they were just another configuration value.

    They aren't.

    A configuration value tells your application how to behave.

    A credential can tell another system that whoever possesses it is allowed to do something.

    That distinction matters.

    A secret is not protected simply because it isn't visible in the screen. If the application needs to possess it, you have to think about who else could eventually extract it.


    The Mobile App Changes the Equation

    A backend server lives in an environment you control. A mobile application does not.

    Once you ship an application, its binary is running on someone else's device. The user can inspect the application package, examine network traffic, debug the application, inspect its resources, and use a range of tools to understand what the application is doing.

    That doesn't mean every mobile application will be reverse-engineered. It means your security model should not depend on nobody doing it.

    This is where a common misunderstanding around .env files appears.

    A developer might think:

    "The key isn't in my Dart code. It's in the environment file."

    That's useful for keeping secrets out of source code, but it doesn't automatically make a credential secret once it is compiled into a mobile application.

    If the application needs the value at runtime, the value has to make its way into something the application can access. You have moved the secret around. You haven't necessarily made it secret.

    This distinction becomes especially important with Flutter applications because developers often use environment variables, build configurations, or compile-time constants as though they provide the same protection that server-side environment variables provide on a backend.

    They don't.


    The Credential Problem Usually Starts Small

    Most credential problems don't begin with someone deliberately building an insecure application.

    They begin with convenience.

    A developer is integrating a maps SDK and needs a key. A developer is connecting an analytics service and needs a token. A developer is testing a third-party API and needs authentication. A developer is building an MVP and doesn't want to spend another afternoon designing a credential-management system.

    So the key goes somewhere convenient.

    Maybe it's a constant. Maybe it's a configuration file. Maybe it's a .env file. Maybe it's a CI variable that eventually gets bundled into the application.

    The first version works. Then the application grows.

    The credential gets copied into another project. Another developer gets access to it. A staging build starts using the production key. Someone forks the repository. The key appears in an old commit. The application reaches production with permissions that were only appropriate for development.

    None of these events needs to be dramatic.

    Security debt is often created through a sequence of completely reasonable decisions.

    When you encounter a credential in a mobile project, don't only ask "where is this stored?" Ask "what can someone do if they obtain it?" The second question usually tells you how seriously the credential needs to be treated.


    What Should Actually Be Kept Out of the App?

    A useful distinction is between identifiers and secrets.

    Some services provide values that identify your application or project but are designed to be distributed to clients. Other credentials grant access to privileged operations and are meant to remain under server-side control.

    The mistake is assuming that every value called an "API key" has the same security properties.

    It doesn't.

    The right question is not simply:

    "Is this an API key?"

    It is:

    "What authority does possession of this value provide?"

    If obtaining the value allows someone to make privileged requests, access private resources, impersonate a trusted service, or consume resources against your account, it deserves a very different treatment from a public client identifier.

    This is also why simply obfuscating a secret isn't a complete security strategy.

    Renaming it. Splitting it across multiple constants. Encoding it. Hiding it behind another function.

    These techniques can make casual inspection harder, but they don't change the fundamental fact that the application needs access to the value.

    Obscurity can slow someone down.

    It shouldn't be the boundary your security depends on.


    A Better Way to Think About Credentials

    Instead of asking where to hide a credential, start by asking whether the mobile application should possess it at all.

    If a privileged operation can happen through your backend, the backend is usually the more appropriate place to hold the sensitive credential.

    The mobile application authenticates the user.

    The backend authenticates itself to the downstream service.

    That creates a much clearer boundary between what the client is allowed to know and what the server is trusted to know.

    Then look at the credentials you do need to distribute.

    Can they be restricted to a specific application?

    Can their permissions be reduced?

    Can they be rotated?

    Can development and production use separate credentials?

    Can a compromised credential be revoked without taking the entire application offline?

    These questions move you from "where do I put the key?" toward the more useful question:

    "What happens if this credential is exposed?"

    That is the question security engineering wants you to ask.

    The safest credential in a mobile application is often the one the mobile application never had to possess in the first place.


    Why This Matters Long After the Key Is Removed

    One of the nastiest parts of credential leakage is that deleting a secret doesn't necessarily undo the exposure.

    If a credential was committed to source control, it may exist in the repository history.

    If it appeared in a build log, someone may have copied it.

    If it was embedded in a released application, previous versions may still be installed.

    If it was shared with a contractor or posted in a ticket, removing it from the original location doesn't retrieve every copy.

    This is why credential rotation matters.

    A leaked credential should generally be treated as compromised rather than assuming nobody saw it.

    The goal isn't to build a system where exposure is impossible. The goal is to reduce the likelihood of exposure, limit what an exposed credential can do, and make recovery possible when something eventually goes wrong.

    That is a much more realistic security posture for a mobile application.

    Security isn't about creating a magical place where nothing can ever leak.

    It's about designing the system so that a leak doesn't automatically become a catastrophe.

    If you want to go deeper into the practical security decisions that sit around credential handling, storage, authentication, communication, and the other places Flutter applications commonly become vulnerable, the Secure Flutter Handbook is designed as a practical reference for exactly that work.


    Written by Mobterest Studio: a mobile engineering brand helping developers and founders build better mobile products through architecture-first thinking. Follow along on YouTube for video breakdowns of the concepts covered here.

    Want to go deeper?

    Free and paid courses, async mentorship, and a beginner's path — everything Mobterest teaches, in one place.

    Explore Engineering Residency →

    More articles

    RSS feed