How To Secure Your API Keys in Android Apps
Most app developers integrate third-party libraries into their apps to access services like Dropbox or YouTube or for logging crashes. The number of these libraries and services is vast, and they typically require some form of authentication, usually through an API key. For security purposes, services generate a public key and a private (or secret) key. Unfortunately, to connect to these services, the private key must be included in the application, leading to significant security risks. Public and private API keys can be extracted from APKs within minutes, a process that can be easily automated.
Given this situation, how can I protect the secret key?
API keys are considered sensitive information that only the developer should have access to. They serve as the gateway to your servers and must be protected. Hackers may attempt to steal your API keys to launch DDoS attacks on your servers or misuse them to access third-party services, potentially incurring significant costs. In this article, we outline four of the most recommended methods to secure an API key in an Android project, starting with the simplest approach and progressing to more advanced techniques.
Wait… Why not use a server to manage all the API keys externally?
While this might seem like an easy solution, in many cases, mobile apps need to initialize all third-party services in the AppConfiguration file before making any REST API calls for an API key.
Of course, this approach works well if your app can afford to wait for the server to provide the necessary keys before initializing your SDKs. However, in scenarios where immediate initialization is required, this method may not be feasible.
So… What can I do?
I recommend using a combination of layers to address all types of vulnerabilities, making it more difficult for attackers to extract your API keys.
Layer 1: Secure API keys from public repositories.
When pushing your code to a public repository (or even a private repository within a company, in our opinion), you should avoid including your API keys as plain strings within your Java/Kotlin classes or resource files. The best practice to prevent API keys from being pushed to a repository, while still being able to use them in your project, is to store them in a custom_name.properties file.
🤔 Tutorial
💚 PROS:
- Your API keys won’t be exposed on GitHub.
- It’s easy to share the API keys within your team.
- Your repository will remain secure.
➖ CONS:
- Your app will still be compiled with the API keys inside.
- Security remains inadequate.
Layer 2: Obfuscation
Moving the API key to a custom_name.properties file is a good practice, but attackers can still decompile your APK file and extract the API keys. To mitigate this risk, it is recommended to use ProGuard or R8 for code obfuscation. This practice increases the complexity for potential attackers attempting to reverse-engineer your application and retrieve sensitive API keys.
🤔 Tutorial:
💚 PROS: It makes it harder for the attacker to find your API-Key inside the decompiled APK source as all classes, methods, and fields will be renamed.
➖ CONS: The API-KEY itself will remain in the form of a plain string and can eventually be found by an attacker.
Layer 3: Use NDK to store API-KEY inside a “.cpp” file.
Some recommend storing your API key in a .cpp file (using C++) and accessing it via the NDK from within your Kotlin/Java code. While our research indicates that the API key stored in the .cpp file can still be accessed through decompiling, this method makes it somewhat more difficult for attackers to obtain your API keys. This approach is particularly effective when combined with obfuscation and encryption techniques.
🤔 Tutorial
💚 PROS: This method adds an extra layer of security, making it more challenging for attackers to decompile your entire project since they would also need to decompile your C++/Native files.
➖ CONS: If an attacker puts in the effort to decompile the C++/Native files, they can still access the content unless it is both obfuscated and encrypted.
Layer 4: Encrypt API keys to prevent reverse engineering
While the previous steps significantly enhance the security of your API keys, the ultimate protection comes from this step. To ensure that attackers cannot access your plain-text API key even after decompiling your SDK, you can encrypt the API key using your preferred encryption algorithm and store the encrypted string within your project. Then, decrypt it whenever needed. Base64 is a common encoding method, though more robust encryption algorithms are recommended. It’s worth noting that DexGuard (a paid and more advanced version of ProGuard) can encrypt values, such as strings, within your project. Using it in conjunction with obfuscation provides maximum security.
🤔 Tutorial
💚 PROS: Even if an attacker decompiles your APK (static reverse engineering) or conducts a basic MITM attack, they will only have the encrypted version of your API key, which is useless without the decryption algorithm.
➖ CONS: If you use a common encryption algorithm, there’s a risk that the attacker could discover it. Additionally, there is a possibility of dynamic reverse engineering or dynamic analysis, such as bypassing certificate pinning and hooking network functions.
Is my API KEY finally secured?
We discussed four different methods to safeguard API keys within your Android application. It is best to use a combination of these approaches.
In our opinion, obfuscation and encryption are essential, while accessing the API key through NDK adds another layer to this security structure. However, keep in mind that no matter how hard you try, your API key is ultimately inside your APK, which is publicly accessible. There are advanced techniques available that eliminate the need for an API key within your Android project, which you can learn more about here.
If you like my posts, you can follow me for more interesting ones!