I'm a bit disappointed about ProGuard.
I am using Gradle to hide my Google MAP API KEYS. I also read this question here Manage Google Maps API Key with Gradle in Android Studio
and did the same. If you follow the accepted answer in this question, your api key won't get obfuscated by ProGuard. The question is why?
There are two good answers. One with ManifestPlaceHolder, one with using @string
Still, if I decompile my app, there is still my weather api key to seen.
I am using private String myweatherapikey = BuildConfig.MY_API_WEATHER_KEY;
and it is amazing (in a bad way) how ProGuard DOES NOT obfuscate this, even when using Gradle.
When you do reverse engineering it looks like private String myweatherapikey ="MY KEY IN PLAIN TEXT";
I am trying to obfuscate my keys since days but nothing worked, even using Gradle. How do you hide your keys ? It really annoys me that all my keys are public, when you decompile my app.
Second question: I think it is impossible to hide your google maps api key. There are 2 keys, one for release-version and one for debug-version. Both of them are stored in src/debug and src/release. You can't hide this, right?
your api key won't get obfuscated by ProGuard
If Proguard or any other tool obfuscates the API key, then it also needs to be de-obfuscated at run-time, otherwise you cannot use it.
No matter if you have an obfuscation mechanism or even an encryption mechanism, an attacker just needs to hook during run-time an instrumentation framework, like Frida, to the code that returns the API after it have been de-obfuscated or decrypted.
Frida:
Inject your own scripts into black box processes. Hook any function, spy on crypto APIs or trace private application code, no source code needed. Edit, hit save, and instantly see the results. All without compilation steps or program restarts.
Another approach for the attacker is to perform a MitM attack in a device he controls or was able to compromise, and intercept all requests between the mobile app and the backend in order to extract the API key from the header, and you can read my article Steal that API Key with a Man in the Midlle Attack to learn how it can be done:
So, in this article you will learn how to setup and run a MitM attack to intercept https traffic in a mobile device under your control, so that you can steal the API key. Finally, you will see at a high level how MitM attacks can be mitigated.
I am trying to obfuscate my keys since days but nothing worked, even using Gradle. How do you hide your keys ? It really annoys me that all my keys are public, when you decompile my app.
From the moment you publish a mobile app into the Google, Apple store or any other store, the binaries can be downloaded and reverse engineered, therefore anything inside of them must be considered to be in the public domain, be it secrets or just code.
My favorite tool to reverse engineer a binary of a mobile app is the MobSF:
Mobile Security Framework (MobSF) is an automated, all-in-one mobile application (Android/iOS/Windows) pen-testing, malware analysis and security assessment framework capable of performing static and dynamic analysis.
You can read my article How to Extract an API key from a Mobile App with Static Binary Analysis to understand how this is done and at same time to learn the several techniques used to try to hide an API key, and see how they are easily bypassed:
During this article we will use the Android Hide Secrets research repository that is a dummy mobile app with API keys hidden using several different techniques.
It's time to look for a more advanced technique to hide the API key in a way that will be very hard to reverse engineer from the APK, and for this we will make use of native C++ code to store the API key, by leveraging the JNI interface which uses NDK under the hood.
Second question: I think it is impossible to hide your google maps api key. There are 2 keys, one for release-version and one for debug-version. Both of them are stored in src/debug and src/release. You can't hide this, right?
If you have read all the links I posted above, then by now you may already have realized that is indeed a Mission Impossible to hide any secret in the binary of your mobile app.
The bottom line is that if is in the binary it can be extracted by static analysis or during run-time.
I am not really familiar with the Google Maps usage in a mobile app, therefore I don't know if its possible to move the calls for it to the backend, because this is what it must be done with any usage of Third Party APIs in a mobile app, otherwise the API key is vulnerable to be extracted and abused, and if you are billed or rate limited based on it, then you may be in trouble when an attacker get hands on your API key.
Currently you are usage of Third Party APIs puts your mobile app in this position:
But preferentially your mobile app should be in this position:
While in the graphics I mention a Reverse Proxy, it could be the backend for your mobile app.
NOTE: The graphics belong to an article that I am currently writing about the use of a Reverse Proxy to access Third Party APIs.
Securing the API keys to access Third Party APIs is much easier when delegating access to them to a reverse proxy or backend we are in control of, and an immediate advantage of this approach is that the API keys for the Thrid Party services are not anymore in the public domain, aka the binary of your mobile app.
So from the graphics we are left with only one API key to secure, the one to access your reverse proxy or backend, where you can employ as many security measures as you can afford to prevent unauthorized access to it.
How do you hide your keys ?
Would not be better to not have any keys at all to hide in your mobile app, because that would be the ideal solution. So the above graphic would look more like:
To be in a position where you don't need to ship any secrets in your mobile app you need to resort to the Mobile App Attestation concept, and from this article section I will extract the relevant bits, that explain it's role:
Before we dive into the role of a Mobile App Attestation service, we first need to understand the difference between what and who is accessing the API server. This is discussed in more detail in this article, where we can read:
The what is the thing making the request to the API server. Is it really a genuine instance of your mobile app, or is it a bot, an automated script or an attacker manually poking around your API server with a tool like Postman?
The who is the user of the mobile app that we can authenticate, authorize and identify in several ways, like using OpenID Connect or OAUTH2 flows.
The role of a Mobile App Attestation service is to authenticate what is sending the requests, thus only responding to requests coming from genuine mobile app instances and rejecting all other requests from unauthorized sources.
In order to know what is sending the requests to the API server, a Mobile App Attestation service, at run-time, will identify with high confidence that your mobile app is present, has not been tampered/repackaged, is not running in a rooted device, has not been hooked into by an instrumentation framework(Frida, xPosed, Cydia, etc.), and is not the object of a Man in the Middle Attack (MitM). This is achieved by running an SDK in the background that will communicate with a service running in the cloud to attest the integrity of the mobile app and device it is running on.
On a successful attestation of the mobile app integrity, a short time lived JWT token is issued and signed with a secret that only the API server and the Mobile App Attestation service in the cloud know. In the case that attestation fails the JWT token is signed with an incorrect secret. Since the secret used by the Mobile App Attestation service is not known by the mobile app, it is not possible to reverse engineer it at run-time even when the app has been tampered with, is running in a rooted device or communicating over a connection that is the target of a MitM attack.
The mobile app must send the JWT token in the header of every API request. This allows the API server to only serve requests when it can verify that the JWT token was signed with the shared secret and that it has not expired. All other requests will be refused. In other words a valid JWT token tells the API server that what is making the request is the genuine mobile app uploaded to the Google or Apple store, while an invalid or missing JWT token means that what is making the request is not authorized to do so, because it may be a bot, a repackaged app or an attacker making a MitM attack.
A great benefit of using a Mobile App Attestation service is its proactive and positive authentication model, which does not create false positives, and thus does not block legitimate users while it keeps the bad guys at bay.
The Mobile App Attestation liberates your mobile app to have an embedded secret in its code, instead now it only needs to pass to the reverse proxy or backend the JWT token it receives from the Attestation. Now the reverse proxy or backend can verify the JWT token, and on successful validation they can fulfill requests with a very high confidence that they are originated from what they expect, a genuine instance of the mobile app, with the added benefit of not exposing the API keys to access your Third Party services.
I cannot resist to recommend the excellent work done in the OWASP - Mobile Security Testing Guide:
The Mobile Security Testing Guide (MSTG) is a comprehensive manual for mobile app security development, testing and reverse engineering.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With