How Little You Really Know About Android Security: A Reverse Engineer's Guide
A short practical article about what reverse engineering Android apps reveals: hardcoded secrets, weak storage, risky logging, network mistakes, and why Android security is not only about permissions.
I have been there.
For a long time, I also wanted to believe that most mobile apps were designed by people who really cared about data safety.
Permissions are used wisely. Encryption protects the data. The backend checks everything. Secrets are not stored inside the app. Production builds do not leak anything useful.
Nice story, bro?
But after diving deeper into Android apps, reverse engineering APK files, checking traffic, reading smali, and looking at what actually ships to users, I started seeing the same pattern again and again.
A lot of apps look secure from the outside.
Inside, things are often much messier.
The Illusion of Android Security
Android has a solid security model.
It has sandboxing. It has permissions. It has app signing. It has Google Play Protect. It has secure storage APIs. It has network security configuration. It has many things that are good and useful.
But here is the problem:
Platform security does not automatically make your app secure.
Android can limit what one app can do to another app. It can isolate processes. It can ask the user for permissions. It can block some obvious abuse.
But Android cannot magically fix bad application design.
If the app logs tokens in production, Android will not save you.
If the API key is hardcoded inside the APK, Android will not save you.
If the backend trusts the mobile app too much, Android will not save you.
If sensitive data is stored in plain text, Android will not save you.
This is the part many people misunderstand.
Android gives developers tools. It does not guarantee that developers use them correctly.
What You See When You Open an APK
An APK is not a black box.
It is more like a packed application bundle.
When you unpack or decompile it, you can often see a lot:
- package structure;
- resources;
- strings;
- permissions;
- API endpoints;
- third-party SDKs;
- app configuration;
- smali code;
- sometimes even SQL queries or business logic.
Of course, modern apps can use obfuscation. Names can be unreadable. Code can be harder to follow.
But obfuscation is not the same as security.
It slows people down. It does not remove the problem.
If a secret is inside the app and the app needs it at runtime, a determined person can usually extract it or observe how it is used.
Hardcoded Secrets Are Still Everywhere
One of the most common problems is hardcoded secrets.
You open the APK, search through strings, and suddenly you find things like:
api_key=
client_secret=
Bearer
firebase
aws
token
private_key
Sometimes these values are harmless public identifiers.
Sometimes they are not.
That is why context matters.
A mobile app should not contain anything that gives real backend access without proper server-side control. If the app has a key, assume the user can get that key too.
This is especially important for:
- API keys;
- cloud service credentials;
- third-party SDK tokens;
- private endpoints;
- encryption keys;
- hidden admin or debug flags.
The correct mindset is simple:
The APK is public.
Even if it is not open source, users can download it, unpack it, inspect it, modify it, and run it.
Logging Too Much Data
Another classic issue is logging.
During development, logs are useful. Developers want to see requests, responses, user IDs, tokens, errors, and internal states.
The problem starts when the same logs survive into production.
You may see logs containing:
- access tokens;
- refresh tokens;
- email addresses;
- phone numbers;
- internal API responses;
- payment or order details;
- debug flags;
- server error messages.
Sometimes logs also reveal how the app works internally.
That is useful for debugging.
It is also useful for attackers.
Production builds should not log sensitive data. Debug logging should be controlled, removed, or heavily limited before release.
Weak Local Storage
Mobile apps often need to store data locally.
That is normal.
The problem is what they store and how they store it.
Bad examples:
Plain text tokens in SharedPreferences
Sensitive JSON files in app storage
User data cached without encryption
Old session data not removed after logout
SQLite databases with readable personal data
Android provides better options, including encrypted storage APIs, but again, the developer has to use them correctly.
Also, encryption is not magic.
If the key is hardcoded in the app, then the encrypted data is not really protected from reverse engineering. You just added one more step.
When testing local storage, I usually ask:
What is stored?
Where is it stored?
Is it sensitive?
Is it encrypted?
Where does the key come from?
Is it removed after logout?
That already catches many real issues.
Network Security Is Not Just HTTPS
Many people think:
We use HTTPS, so traffic is secure.
Not always.
HTTPS is a baseline. It is not the whole story.
Things to check:
- Does the app ever use plain HTTP?
- Are sensitive parameters sent in URLs?
- Are tokens sent to third-party domains?
- Is certificate validation implemented correctly?
- Does the app trust user-installed certificates?
- Is SSL pinning used where it makes sense?
- Can requests be replayed?
- Does the backend validate everything server-side?
This is where tools like Charles Proxy, Burp Suite, mitmproxy, Frida, and Objection become useful in a lab environment.
You are not just “watching traffic.”
You are checking whether the app leaks data, whether requests can be modified, and whether the backend actually protects itself.
Permissions Do Not Tell the Whole Story
Permissions matter, but they are not enough.
An app can request only a few permissions and still leak data through bad API calls.
Another app can request many permissions but use them responsibly.
So permission review is only one part of Android security testing.
Still, it is useful to check the manifest:
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.READ_CONTACTS" />
Ask simple questions:
- Does the app really need this permission?
- Is it requested at the right time?
- Is there a clear user-facing reason?
- What happens if the user denies it?
- Is permission-protected data sent somewhere?
Permissions are the start of the conversation, not the end.
The Backend Is Often the Real Problem
A mobile app is only one part of the system.
Many serious issues happen because the backend trusts the app too much.
Bad assumptions:
The user cannot modify the request.
The app will always send correct values.
The hidden button is not accessible.
The client-side check is enough.
The API endpoint is private because it is not documented.
All of those assumptions are dangerous.
A user can modify requests.
A user can patch the APK.
A user can call the API directly.
A user can bypass client-side checks.
That is why security-critical logic belongs on the server.
The mobile app should be treated as an untrusted client.
Vibe-Coding Makes This Worse
There is also a newer problem: vibe-coding.
People generate code fast, glue libraries together, and ship features without fully understanding what the code does.
This is not only an AI problem. Copy-paste development existed long before AI tools.
But AI makes it easier to produce working-looking code quickly.
And working-looking code is not the same as secure code.
Common results:
- secrets placed in source code;
- insecure examples copied into production;
- weak crypto snippets;
- missing certificate validation;
- bad error handling;
- over-permissive network configuration;
- debug code left inside release builds.
Fast development is fine.
Shipping unknown security behavior is not.
What Reverse Engineering Teaches You
Reverse engineering does not magically make you a security expert.
But it changes how you look at apps.
You stop trusting the UI.
You stop assuming that hidden means protected.
You stop believing that “encrypted” always means safe.
You start asking better questions:
What is inside the APK?
What does the app store?
What does it send?
What can be modified?
What does the backend trust?
What happens if I bypass the UI?
That mindset is valuable not only for pentesters.
It is also useful for QA engineers, Android developers, automation engineers, and anyone who works with mobile apps.
A Small Practical Checklist
If you want to start testing Android apps more seriously, begin with this:
Static Analysis
Check the APK without running it.
Look for:
- hardcoded secrets;
- API endpoints;
- debug flags;
- exported components;
- suspicious permissions;
- old libraries;
- weak crypto usage;
- interesting strings.
Useful tools:
- jadx;
- apktool;
- MobSF;
- grep/ripgrep;
- Android Studio APK Analyzer.
Dynamic Analysis
Run the app and observe behavior.
Check:
- logs;
- local storage;
- network traffic;
- runtime checks;
- root detection;
- certificate pinning;
- API behavior.
Useful tools:
- adb;
- Charles Proxy;
- Burp Suite;
- mitmproxy;
- Frida;
- Objection.
Backend Behavior
Do not stop at the app.
Check:
- authorization;
- object access control;
- replay attacks;
- parameter tampering;
- missing server-side validation;
- predictable IDs;
- weak session handling.
A pretty mobile UI can hide a very weak backend.
Conclusion
Android security is not only about permissions.
Real Android security lives in the details: what is inside the APK, what is stored locally, what is logged, what is sent over the network, and what the backend decides to trust.
Reverse engineering makes this visible.