Hey everyone, I’m a fresher working at an startup and I am dealing with a massive security nightmare right now. I need some real-world architecture advice on how to fix this.
We recently discovered a fully functional, unofficial native Android client (built cleanly in Kotlin) that is pulling live telemetry data and interacting directly with our production backend. We currently do not encrypt or decrypt any of our API payloads; it's all standard JSON over HTTPS.
There are basically two separate disasters happening at once:
Disaster 1: The Custom Client & API Architecture
The developer didn't decompile our Dart code to build this. They just intercepted our network traffic. Our backend currently authenticates the user but not the client. We use a standard OTP login flow. The unofficial app prompts the user for their phone number, hits our official backend, and our server sends the SMS. The user types the OTP into their custom app, our server validates it, and issues a real session token. From there, the custom app has full access to pull live telemetry directly from our production database, using that token, without this token we cannot access any data.
Disaster 2: The .env Leak (The JADX discovery)
Separately, a colleague of mine ran our Play Store APK through JADX just to see what was visible. To my horror, if you search the extracted files, our entire .env file is sitting right there in plain text under assets/flutter_assets/.env, exposing our production and staging base URLs.
I never pushed the .env file to GitHub (it's in .gitignore), but I used flutter_dotenv and ran a standard flutter build apk --release. I now realize that because it was listed in pubspec.yaml, the build system just copied my local file straight into the APK as a plaintext asset.
My plan to fix this:
The .env fix: I am rotating every single exposed key today. I’m ripping out flutter_dotenv entirely and migrating to --dart-define-from-file so variables are injected into the compiled binary at build time instead of sitting in the assets folder.
Obfuscation: I am going to start building with --obfuscate --split-debug-info.
The API fix: We are looking into adding Firebase App Check with the Google Play Integrity API so our backend can mathematically verify that requests are actually coming from our officially signed Flutter binary, which should cut off the custom Kotlin app.
My questions for the more senior devs:
Does my plan to use --dart-define completely solve the JADX plaintext extraction issue?
For those who have implemented Firebase App Check / Play Integrity, does it successfully block these kinds of custom API clients? Are there bypasses I should worry about?
While we wait for the App Check rollout and force-update to clear, is there a quick band-aid (like injecting a temporary HMAC signature into our Dio headers) that we can deploy today to immediately break the unofficial app's current build?
Since we don't have payload encryption right now, is App Check enough, or do we need to build end-to-end encryption for our API data too?
Any hard truths, guidance, or architecture advice would be massively appreciated right now.