Best practice for storing and protecting private API keys in applications

190,837

Solution 1

  1. As it is, your compiled application contains the key strings, but also the constant names APP_KEY and APP_SECRET. Extracting keys from such self-documenting code is trivial, for instance with the standard Android tool dx.

  2. You can apply ProGuard. It will leave the key strings untouched, but it will remove the constant names. It will also rename classes and methods with short, meaningless names, where ever possible. Extracting the keys then takes some more time, for figuring out which string serves which purpose.

    Note that setting up ProGuard shouldn't be as difficult as you fear. To begin with, you only need to enable ProGuard, as documented in project.properties. If there are any problems with third-party libraries, you may need to suppress some warnings and/or prevent them from being obfuscated, in proguard-project.txt. For instance:

    -dontwarn com.dropbox.**
    -keep class com.dropbox.** { *; }
    

    This is a brute-force approach; you can refine such configuration once the processed application works.

  3. You can obfuscate the strings manually in your code, for instance with a Base64 encoding or preferably with something more complicated; maybe even native code. A hacker will then have to statically reverse-engineer your encoding or dynamically intercept the decoding in the proper place.

  4. You can apply a commercial obfuscator, like ProGuard's specialized sibling DexGuard. It can additionally encrypt/obfuscate the strings and classes for you. Extracting the keys then takes even more time and expertise.

  5. You might be able to run parts of your application on your own server. If you can keep the keys there, they are safe.

In the end, it's an economic trade-off that you have to make: how important are the keys, how much time or software can you afford, how sophisticated are the hackers who are interested in the keys, how much time will they want to spend, how much worth is a delay before the keys are hacked, on what scale will any successful hackers distribute the keys, etc. Small pieces of information like keys are more difficult to protect than entire applications. Intrinsically, nothing on the client-side is unbreakable, but you can certainly raise the bar.

(I am the developer of ProGuard and DexGuard)

Solution 2

Few ideas, in my opinion only first one gives some guarantee:

  1. Keep your secrets on some server on internet, and when needed just grab them and use. If user is about to use dropbox then nothing stops you from making request to your site and get your secret key.

  2. Put your secrets in jni code, add some variable code to make your libraries bigger and more difficult to decompile. You might also split key string in few parts and keep them in various places.

  3. use obfuscator, also put in code hashed secret and later on unhash it when needed to use.

  4. Put your secret key as last pixels of one of your image in assets. Then when needed read it in your code. Obfuscating your code should help hide code that will read it.

If you want to have a quick look at how easy it is to read you apk code then grab APKAnalyser:

http://developer.sonymobile.com/knowledge-base/tool-guides/analyse-your-apks-with-apkanalyser/

Solution 3

Another approach is to not have the secret on the device in the first place! See Mobile API Security Techniques (especially part 3).

Using the time honored tradition of indirection, share the secret between your API endpoint and an app authentication service.

When your client wants to make an API call, it asks the app auth service to authenticate it (using strong remote attestation techniques), and it receives a time limited (usually JWT) token signed by the secret.

The token is sent with each API call where the endpoint can verify its signature before acting on the request.

The actual secret is never present on the device; in fact, the app never has any idea if it is valid or not, it juts requests authentication and passes on the resulting token. As a nice benefit from indirection, if you ever want to change the secret, you can do so without requiring users to update their installed apps.

So if you want to protect your secret, not having it in your app in the first place is a pretty good way to go.

Solution 4

Old unsecured way:

Follow 3 simple steps to secure the API/Secret key (Old answer)

We can use Gradle to secure the API key or Secret key.

1. gradle.properties (Project properties): Create variable with key.

GoogleAPIKey = "Your API/Secret Key"

2. build.gradle (Module: app) : Set variable in build.gradle to access it in activity or fragment. Add below code to buildTypes {}.

buildTypes.each {
    it.buildConfigField 'String', 'GoogleSecAPIKEY', GoolgeAPIKey
}

3. Access it in Activity/Fragment by app's BuildConfig:

BuildConfig.GoogleSecAPIKEY

Update:

The above solution is helpful in the open-source project to commit over Git. (Thanks to David Rawson and riyaz-ali for your comment).

As per Matthew and Pablo Cegarra's comments, the above way is not secure and Decompiler will allow someone to view the BuildConfig with our secret keys.

Solution:

We can use NDK to Secure API Keys. We can store keys in the native C/C++ class and access them in our Java classes.

Please follow this blog to secure API keys using NDK.

A follow-up on how to store tokens securely in Android

Solution 5

Adding to @Manohar Reddy solution, firebase Database or firebase RemoteConfig (with Null default value) can be used:

  1. Cipher your keys
  2. Store it in firebase database
  3. Get it during App startup or whenever required
  4. decipher keys and use it

What is different in this solution?

  • no credintials for firebase
  • firebase access is protected so only app with signed certificate have privilege to make API calls
  • ciphering/deciphering to prevent middle man interception. However calls already https to firebase
Share:
190,837
Basic Coder
Author by

Basic Coder

Great coders are todays rockstars!

Updated on July 08, 2022

Comments

  • Basic Coder
    Basic Coder almost 2 years

    Most app developers will integrate some third party libraries into their apps. If it's to access a service, such as Dropbox or YouTube, or for logging crashes. The number of third party libraries and services is staggering. Most of those libraries and services are integrated by somehow authenticating with the service, most of the time, this happens through an API key. For security purposes, services usually generate a public and private, often also referred to as secret, key. Unfortunately, in order to connect to the services, this private key must be used to authenticate and hence, probably be part of the application. Needless to say, that this faces in immense security problem. Public and private API keys can be extracted from APKs in a matter of minutes and can easily be automated.

    Assuming I have something similar to this, how can I protect the secret key:

    public class DropboxService  {
    
        private final static String APP_KEY = "jk433g34hg3";
        private final static String APP_SECRET = "987dwdqwdqw90";
        private final static AccessType ACCESS_TYPE = AccessType.DROPBOX;
    
        // SOME MORE CODE HERE
    
    }
    

    What is in your opinion the best and most secure way to store the private key? Obfuscation, encryption, what do you think?

  • Matt Wolfe
    Matt Wolfe about 11 years
    if user can decompile app though they could likely determine request that was made to your own server and simply execute that to get secret. No silver bullet here but take a few steps and I bet you'll be fine! If your app is super popular though maybe not.. Great ideas!
  • marcinj
    marcinj about 11 years
    yeah, number 1 gives no guarantee.
  • Igor Čordaš
    Igor Čordaš about 10 years
    I really like the idea of hiding keys inside images. +1
  • Sanandrea
    Sanandrea over 9 years
    I prefer number 2, take a look at: github.com/sanandrea/CSecretKey waiting for some comments/advices
  • Admin
    Admin almost 9 years
    Is this a local server being talked about in the fifth point? Wouldn't this need a computer that requires to stay on always? Sorry, I'm a newbie. Can I use something like Parse?
  • Alan
    Alan about 8 years
    @EricLafortune does it not make a difference if the private key string is stored in the Java class vs in the String resource XML?
  • Dr.jacky
    Dr.jacky about 8 years
    @MarcinJędrzejewski Would you like to explain more(with sample or snipped code preferably) about forth solution? Thank you.
  • marcinj
    marcinj about 8 years
    @Mr.Hyde this is called steganography, its way too complex to give a sample code here, you can find examples on google. I have found one here: dreamincode.net/forums/topic/27950-steganography. The idea is great but since apk code can be decompiled it spoils its beauty.
  • David Thomas
    David Thomas almost 8 years
    @EricLafortune Is it now possible to use the Android Keystore system to securely store the keys? ( developer.android.com/training/articles/keystore.html )
  • Dr.jacky
    Dr.jacky almost 8 years
    @MarcinJędrzejewski It's interesting, there are too many algorithm for steganography. Do you know any tools that can use on both windows os side, and android? I want to encode message in PC, and just use deciding method inside my android application.
  • Sagar Trehan
    Sagar Trehan almost 8 years
    @DavidThomas: Did you tried using keystore. I want to obfuscate a API key written in Java class. Please reply
  • David Thomas
    David Thomas almost 8 years
    @SagarTrehan no, I haven't tried the keystore approach yet. There is a popular utility called "dexguard" that may be of interest to you there.
  • Weishi Z
    Weishi Z over 7 years
    Is Android KeyStore useful for this? (developer.android.com/training/articles/…)
  • user2966445
    user2966445 over 7 years
    But how do you prevent the rest of the world from calling the server?
  • Nick
    Nick over 7 years
    If by "the server" you mean your webserver where the credentials are - you can use any method you want. simple authentication with username / password, oauth, active directory, etc. etc. Really depends on your application.
  • user2966445
    user2966445 over 7 years
    Maybe I'm missing something, but doesn't this still require storing credentials within the app?
  • Nick
    Nick over 7 years
    No, the dropbox credentials are stored on your application server, the app talks to your application server, which handles any dropbox requests.
  • user2966445
    user2966445 over 7 years
    Right, but you said the app would authenticate with the server first. Doesn't this mean storing another set of credentials in the app? I understand the server would handle the actual dropbox calls.
  • Nick
    Nick over 7 years
    Well, it could mean that, but it a completely separate authorization. But you don't have to. The usecase I am talking about is that your app user would have a login to your app, say using facebook or twitter. You don't store their credentials in your app, you don't even know them. That authorization process allows them access to your api server, which has the credentials to dropbox, but no app or user has access to them directly.
  • david72
    david72 over 7 years
    Users can easily make function calls to the shared library and get whatever is hiding there. No need to decopile it.
  • david72
    david72 over 7 years
    According to androidauthority.com/… there is no safe way to do it in android at the moment.
  • Sumit
    Sumit over 7 years
    yeah i already had stored in image file and get key through image as BufferReader, not secure but good to go
  • Johny19
    Johny19 over 7 years
    @AhmedAwad Don't understand why this is having 3 upvote. any one could easily decompile the app and see how the ndk entry point are being called :/
  • Mercury
    Mercury about 7 years
    The problem is - to reach that server which contains all the secrects i should use another Secret Key - I wonder where i will keep that ? ;) What i am trying to say is - This is also not the best solution (don't think there is an ideal solution here)
  • Bernard Igiri
    Bernard Igiri about 7 years
    That's not true, your server is completely under your control. What it requires is entirely up to you.
  • Mercury
    Mercury about 7 years
    Can you explain here how client can encrypt the data he wants to send to the server, while the keys are in the server side ? and if your answer will be - The server sends keys to the client - So that must be secured as well! so again no magic solution! can't you see?!
  • Ken
    Ken about 7 years
    @Mercury lolz, I came here and immediately after seeing the "keep your keys in your private server" I just think the same. You also need to secure the key to your private server or the hacker can just go to your server and grab the key. At the end you have a vicious cycle without solution. It's funny cause I came here to find a simple solution of hiding the API key to MY server. And they are suggesting I should make another server for that and then run into the some problems over and over again
  • Bernard Igiri
    Bernard Igiri about 7 years
    @Ken your If you want to learn about keeping a server secure this is good place to start: cyberciti.biz/tips/linux-security.html Regardless, SSH with key authentication and VPN/firewall are the basic ways to access a server remotely without giving out access to the world. The SSH key sits on your personal computer.
  • Ken
    Ken about 7 years
    @BernardIgiri, Aren't you missing the whole point of it all? The whole point is to be able to access the server from the mobile application you're building without making it vulnerable because everyone who has the app can retrieve the access keys and tokens by dissambling the app. What have ssh with key authentication and keys been stored on your PC to do with that?
  • Bernard Igiri
    Bernard Igiri about 7 years
    @Ken so the issue you are trying to resolve is preventing others from using your server? I only know of one solution, authentication. Users will have to create an account and login to access the server. If you don't want people to have to put in their information, you could have the app automate it. The app could generate a random login token on the phone, send the request to the server with the phone #, and the server can respond with a random pin to the phone. Upon verification of the pin the account is created, and the token is all that is needed from there on.
  • Ken
    Ken about 7 years
    @BernardIgiri and then again we are back to square 1. Let's assume, the phone creates a random login and the server accepts that and sends a pin(This is the so called private server we are talking). Then the person who disassembles your app sees that all it takes to access your private server is just some random login that he can create himself. Tell me what stops him from creating one and accessing your server? In fact what is the difference between your solution and actually storing a login or api key to the main server (whose credentials we wanted to store in our private server)
  • Bernard Igiri
    Bernard Igiri about 7 years
    @ken The random number is authenticated against the phone number and physical access to its text messages. If someone cheats you, you have their information. If that's not good enough force them to create a full user account and password. If that's not good enough get a credit card too. If that's not good enough have them call in. If that's not good enough meet them face to face. How secure/inconvenient do you want to be?
  • pete
    pete almost 7 years
    How does #1 help? The attacker can just query the server for the secret key before querying your main service, so it's just as easy as before to impersonate your app isn't it?
  • pete
    pete almost 7 years
    How does #3 work? How do you "unhash"
  • pete
    pete almost 7 years
    I don't understand #5. Doesn't it have the same exact problems as the original problem?
  • Bart van Ingen Schenau
    Bart van Ingen Schenau almost 7 years
    @pete: #5 means that a server you own acts as a go-between in the authentication. The API keys will be stored only on your server and not be included in any code that leaves your control.
  • Stoycho Andreev
    Stoycho Andreev over 6 years
    This answer is almost one of the best options, but the author should mention that it is very important that you should include a call (inside your NDK library) to see if the checksum matches your APK as otherwise someone can call your NDK library outside of your app
  • Google
    Google over 6 years
    storing a key in gradle file is secure?
  • David Rawson
    David Rawson over 6 years
    @Google gradle.properties shouldn't be checked in to Git so this is a way of keeping the secret out of committed source code, at least
  • Yann39
    Yann39 over 6 years
    What about storing the keys in a database (e.g. embedded sqlite) using a strong key derivation algorithm (e.g. bcrypt, argon2, ...)? Wouldn't it act the same as keeping them everywhere else (even on server) as the only way to get them would be to run the code and intercept the decoding in the proper place?
  • riyaz-ali
    riyaz-ali about 6 years
    This doesn't prevent the API key from being bundled into the resulting apk (it will be add to the generated BuildConfig file), although this is definitely a good idea to manage different API keys (for e.g. in an open source project)
  • Matthew
    Matthew about 6 years
    Using a Java Decompiler will allow someone to view the BuildConfig file and "GoogleSecAPIKEY"
  • the_joric
    the_joric about 6 years
    but what about credentials to firebase?
  • iceman
    iceman about 6 years
    Your BuildConfig.java file will have the key in plain text form. This is no better than what the OP is already doing.
  • Konjengbam
    Konjengbam about 6 years
    Unfortunately, Firebase database doesn't work in China.
  • user924
    user924 almost 6 years
    doesn't make sense, attackers can see you firebase details from decompiled code and get any data from your datababse
  • SocketByte
    SocketByte almost 6 years
    @Sniper that would be great, except it has a big problem. How do you know what file is "calling" the native method? If you hard-code the name of the apk to check, great, but what if I make my "hack" apk the same folder as the "good" apk? It will check that the "good" apk has good checksum, and it will allow me to execute that native method. Unless there's a way to know the caller file from JNI/C++ side, then it's pointless as the other options.
  • ortonomy
    ortonomy over 5 years
    You just copy and pasted this from here: hackernoon.com/mobile-api-security-techniques-682a5da4fe10 without acknowledging the source.
  • Ayman Al-Absi
    Ayman Al-Absi over 5 years
    I think this is the best solution as firebase Apps uses SHA1 to allow access to server. Decompiling the code will not help to make call to firebase because hacker new app should use exact app stamp to access firebase. Also, stored key should be ciphered before storing in firebase DB and deciphered once received to avoid middle man interception.
  • sudocoder
    sudocoder over 5 years
    @Bart van ignen Schenau, I have the same question as Pete. What's to prevent someone from decompiling your app and seeing how function() works. They could then copy the algorithm and communicate with your server to execute bad things.
  • Bart van Ingen Schenau
    Bart van Ingen Schenau over 5 years
    @sudocoder: Naturally, your server would validate that the requests come from a authenticated user.
  • sudocoder
    sudocoder over 5 years
    @BartvanIngenSchenau how would i send requests to a server to validate that I am indeed authenticated? I can think of a solution... I'd send a private key credential... but isn't that the original problem we're trying to solve?
  • Exadra37
    Exadra37 about 5 years
    @ortonomy I agree that he should have quoted the article you linked, but he may have forgot it because both work in same place...
  • ThePragmatist
    ThePragmatist about 5 years
    Also Skip's article and the blog post they are based on came out a week after my answer.
  • Csaba Toth
    Csaba Toth about 5 years
    When you get the secret from the firebase database through the network, how does that more secure than getting the same secret from another web service through a secure (https) channel? Can you explain?
  • Manohar
    Manohar about 5 years
    @CsabaToth see comment of Ayman Al-Absi . Also I am not saying Firebase Database is 100% attack proof but it is better than calling web service for key .
  • shareef
    shareef about 5 years
    good suggestion, but for government sector apps, its not prefered option.
  • MEE
    MEE over 4 years
    @sudocoder You ask the user for login credentials to authenticate. If your app does not require user/client authentication, then perhaps all you need is employ rate-limiting and DoS defenses at the backend server.
  • Noor Hossain
    Noor Hossain over 4 years
    but in this way (from Firebase database) , How can I use the key in Manifest ?
  • Manohar
    Manohar over 4 years
    @NoorHossain what key are you talking about ?
  • sudocoder
    sudocoder over 4 years
    Correct me if i'm wrong, #1 works because you log in and you're granted a session token. Then you make every call through your server with this session token - the server fetches and returns information for you. That means if I want to call dropbox's api i'd have to go through my server first with my granted session token.
  • marcinj
    marcinj over 4 years
    @sudocoder not really, its explained in comments - this idea was about using request to your server to get api key (or any other secret) over https connection. This way you dont keep secret in apk, but as explained in comment, hacker might find out the http request and try to call it. This is where security over obsurity starts,:-) you could pack your secret in the image using some form of steganografy and decode it in native c++ code. Still clever hacker will simply print this key before the dropbox request is formed - after all that obsurity....
  • marcinj
    marcinj over 4 years
    @sudocoder your idea about using own server as a proxy to dropbox seems very nice, api keys will be protected this way - but it might complicate use of dropbox apis. In recent times I found that fight with hackers is difficult, what worked for me was to analyze what hackers have changed in apk to break security and do fixes around those places to protect them. It takes few hours, maybe a day. Release new updated apk quite often and make sure your content will work only with newest (or two last versions..) versions so that pirates will not be able to use old cracked ones.
  • mtrewartha
    mtrewartha over 4 years
    The very large, bold text at the top of your answer should probably not say "secure" if the solution is quite obviously insecure, as pointed out. I know you have an "update" section in there, but many people proooobably don't read that.
  • Ashi
    Ashi over 4 years
    The problem persists when you want to access the authentication service. It will give you a client id and client secret. Where should we save them?
  • Ashi
    Ashi over 4 years
    with all do respect with this solution we are still the first square. Instead of using credentials , you suggest to use a certificate. Whoever is capable of stealing your credentials is capable of stealing your signed certificate.
  • Ashi
    Ashi over 4 years
    One advantage though, with the suggested solution, we are adding one more complication in front of the hacker.
  • Ankur
    Ankur over 4 years
    I'll +1 this because, keeping keys on different server (from API server) makes total sense.
  • Kibotu
    Kibotu over 4 years
    doesn't solve private apis where you need to authenticate to your api first in order to use it. where do you get the credentials for all app users?
  • Rahul Bansal
    Rahul Bansal almost 4 years
    Firebase database has significant downtime.
  • Simranjeet Singh
    Simranjeet Singh over 3 years
    @Ashi client id is somewhat obfuscated in a way that only api endpoint knows on how to extract data from obfuscated data like if only some of the characters of client id(where client id is actual client id + some more data to create obfuscated string )only means the actual data but if hacker tries to change or make changes to client id, he doesn't know which data actually represents client id because that is actually known only to api end point on how client id has been obfuscated and how to extract useful data from client id which actually represents client id.... hope u get my point
  • nelsola
    nelsola over 3 years
    i believe @Bernardlgiri answer is quite good, as the server will make the api call to only a specific api endpoints and will only allow some calls based on the authentication and also firebase security rules. The response from the API call is what would be sent back and not the API key. Kindly enlighten me if this method exposes exploits.
  • Ben-J
    Ben-J over 3 years
    We provide a free and open-source Dexguard alternative to deeply hide secret keys in Android. This is a gradle plugin that uses the NDK and XOR operator to prevent from reverse-engineering : github.com/klaxit/hidden-secrets-gradle-plugin
  • Ben-J
    Ben-J over 3 years
    About the NDK solution we have created a free gradle plugin that obfuscate and store your key in C++ files, accessible from your Java code. All the details here : github.com/klaxit/hidden-secrets-gradle-plugin
  • Carlos Iglesias
    Carlos Iglesias almost 3 years
    This answer miss the man-in-the-middle security issues.
  • Code Name Jack
    Code Name Jack about 2 years
    We never keep our private certs in source code, so chance no of stealing, right?
  • c.fogelklou
    c.fogelklou about 2 years
    I think this is getting close to the best solution, but combine this with statically linked NDK code that hashes the "running app name" and uses this resulting hash to decrypt the secret.
  • c.fogelklou
    c.fogelklou about 2 years
    Add hooks back from C++ side to use JNI to get the app's package name, hash it, and use that as a decryption key to decrypt the secret on the C++ side using nacl or libsodium.
  • swati vishnoi
    swati vishnoi almost 2 years
    No4 is intersting but can't say it's secure.
  • swati vishnoi
    swati vishnoi almost 2 years
    I genrate QR code for the multiple keys and store it as one single image, when i need to use the key i call the method which can read the QR code. yes every method will Obfuscate.
  • Sri Harsha Chilakapati
    Sri Harsha Chilakapati almost 2 years
    I was able to learn decompiling those NDK binaries in less than an hour by just searching in YouTube. Look for Binary Exploitation playlist by LiveOverflow. Just the videos 4, 5, 6 and 7 each 10 minutes long are enough and you only use free Foss tools. Real extraction only takes about 20 minutes.