Closed. This question is opinion-based. It is not currently accepting answers.
Want to improve this question? Update the question so it can be answered with facts and citations by editing this post.
Closed 3 years ago.
Improve this question
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?
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.
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.
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.
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.
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)
Few ideas, in my opinion only first one gives some guarantee:
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.
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.
use obfuscator, also put in code hashed secret and later on unhash it when needed to use.
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/
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.
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
Adding to #Manohar Reddy solution, firebase Database or firebase RemoteConfig (with Null default value) can be used:
Cipher your keys
Store it in firebase database
Get it during App startup or whenever required
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
The App-Secret key should be kept private - but when releasing the app
they can be reversed by some guys.
for those guys it will not hide, lock the either the ProGuard the code. It is a refactor and some payed obfuscators are inserting a few bitwise operators to get back the jk433g34hg3
String. You can make 5 -15 min longer the hacking if you work 3 days :)
Best way is to keep it as it is, imho.
Even if you store at server side( your PC ) the key can be hacked and printed out. Maybe this takes the longest? Anyhow it is a matter of few minutes or a few hours in best case.
A normal user will not decompile your code.
One possible solution is to encode the data in your app and use decoding at runtime (when you want to use that data). I also recommend to use progaurd to make it hard to read and understand the decompiled source code of your app . for example I put a encoded key in the app and then used a decode method in my app to decode my secret keys at runtime:
// "the real string is: "mypassword" ";
//encoded 2 times with an algorithm or you can encode with other algorithms too
public String getClientSecret() {
return Utils.decode(Utils
.decode("Ylhsd1lYTnpkMjl5WkE9PQ=="));
}
Decompiled source code of a proguarded app is this:
public String c()
{
return com.myrpoject.mypackage.g.h.a(com.myrpoject.mypackage.g.h.a("Ylhsd1lYTnpkMjl5WkE9PQ=="));
}
At least it's complicated enough for me. this is the way I do when I have no choice but store a value in my application. Of course we all know It's not the best way but it works for me.
/**
* #param input
* #return decoded string
*/
public static String decode(String input) {
// Receiving side
String text = "";
try {
byte[] data = Decoder.decode(input);
text = new String(data, "UTF-8");
return text;
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
return "Error";
}
Decompiled version:
public static String a(String paramString)
{
try
{
str = new String(a.a(paramString), "UTF-8");
return str;
}
catch (UnsupportedEncodingException localUnsupportedEncodingException)
{
while (true)
{
localUnsupportedEncodingException.printStackTrace();
String str = "Error";
}
}
}
and you can find so many encryptor classes with a little search in google.
This example has a number of different aspects to it. I will mention a couple of points that I don't think have been explicitly covered elsewhere.
Protecting the secret in transit
The first thing to note is that accessing the dropbox API using their app authentication mechanism requires you to transmit your key and secret. The connection is HTTPS which means that you can't intercept the traffic without knowing the TLS certificate. This is to prevent a person intercepting and reading the packets on their journey from the mobile device to the server. For normal users it is a really good way of ensuring the privacy of their traffic.
What it is not good at, is preventing a malicious person downloading the app and inspecting the traffic. It is really easy to use a man-in-the-middle proxy for all traffic into and out of a mobile device. It would require no disassembly or reverse engineering of code to extract the app key and secret in this case due to the nature of the Dropbox API.
You could do pinning which checks that the TLS certificate you receive from the server is the one you expect. This adds a check to the client and makes it more difficult to intercept the traffic. This would make it harder to inspect the traffic in flight, but the pinning check happens in the client, so it would likely still be possible to disable the pinning test. It does make it harder though.
Protecting the secret at rest
As a first step, using something like proguard will help to make it less obvious where any secrets are held. You could also use the NDK to store the key and secret and send requests directly, which would greatly reduce the number of people with the appropriate skills to extract the information. Further obfuscation can be achieved by not storing the values directly in memory for any length of time, you can encrypt them and decrypt them just before use as suggested by another answer.
More advanced options
If you are now paranoid about putting the secret anywhere in your app, and you have time and money to invest in more comprehensive solutions, then you might consider storing the credentials on your servers (presuming you have any). This would increase the latency of any calls to the API, as it will have to communicate via your server, and might increase the costs of running your service due to increased data throughput.
You then have to decide how best to communicate with your servers to ensure they are protected. This is important to prevent all of the same problems coming up again with your internal API. The best rule of thumb I can give is to not transmit any secret directly because of the man-in-the-middle threat. Instead you can sign the traffic using your secret and verify the integrity of any requests that come to your server. One standard way of doing this is to compute an HMAC of the message keyed on a secret. I work at a company that has a security product that also operates in this field which is why this sort of stuff interests me. In fact, here is a blog article from one of my colleagues that goes over most of this.
How much should I do?
With any security advice like this you need to make a cost/benefit decision about how hard you want to make it for someone to break in. If you are a bank protecting millions of customers your budget is totally different to someone supporting an app in their spare time. It is virtually impossible to prevent someone from breaking your security, but in practice few people need all of the bells and whistles and with some basic precautions you can get a long way.
Whatever you do to secure your secret keys is not going to be a real solution. If developer can decompile the application there is no way to secure the key, hiding the key is just security by obscurity and so is code obfuscation. Problem with securing a secret key is that in order to secure it you have to use another key and that key needs to also be secured. Think of a key hidden in a box that is locked with a key. You place a box inside a room and lock the room. You are left with another key to secure. And that key is still going to be hardcoded inside your application.
So unless the user enters a PIN or a phrase there is no way to hide the key. But to do that you would have to have a scheme for managing PINs happening out of band, which means through a different channel. Certainly not practical for securing keys for services like Google APIs.
The most secure solution is to keep your keys on a server and route all requests needing that key through your server. That way the key never leaves your server, so as long as your server is secure then so is your key. Of course there is a performance cost with this solution.
The only true way to keep these private is to keep them on your server, and have the app send whatever it is to the server, and the server interacts with Dropbox. That way you NEVER distribute your private key in any format.
Ages old post, but still good enough. I think hiding it in an .so library would be great, using NDK and C++ of course. .so files can be viewed in a hex editor, but good luck decompiling that :P
Keep the secret in firebase database and get from it when app starts ,
It is far better than calling a web service .
I have seven virtual products in my game and would like to give one away each day or so to people who visit my website.
Bob Smith is correct that this will work, but depending on the value of these in-app-purchases you may want to err towards something more complex.
If there's nothing generated about these codes, i.e. my code is the same as yours, then any one person can just give this code away and it will work for everyone. Another suggestion to keep in mind is to make the code a hash of some predetermined secret value, and the unique identifier of the app.
For example, if every app has in it an ID, your code might be:
md5(userId.toString + mySecret);
Because a hash is one way, nobody can get your secret back out and compute their own codes. They have to submit their app code through your service to get a code specific for them.
You can continue to layer in features too. You could include secret identifiers for each purchase, so that one algorithm generates unique codes for each purchase.
Of course, if there is only one secret, it would also have to be hard coded into the app, which is not utterly secure. This is a problem for both Bob's system and mine, and should that secret get public the entire system falls apart. You could look into all sorts of different encryption techniques to come up with more and more clever systems, if you need that level of security.
Yes you can do that, what you should do is send the recipient of the code the code via email or someway, then in the app have a spot where the person can enter the code in. Similar to entering in cheat codes in a videogame. If their code they enter matches one that you made valid, you should grant them access to the virtual product.
I've figured I have to do some kind of API where the Android app sends parameters to a php-file, or similar.
I'm thinking of an app that would have some sort of high score list. How would I go about making sure that the submitted high score values are in fact achieved through the app? Say that the person achieves 9001 points. Now if this only results in some sort of file.php?score=9001 call, I'm not feeling very secure about people finding the php-location and submitting whatever they like.
Any input on this kind of problem? Thank you.
There is no way to make this 100% secure since you have no way of verifying where a request came from, but there is a way to make it slightly harder to hack the highscores. It's still basically security through obscurity but it requires digging in to the actual app binary rather than just looking at the network connections.
First, pick a "password" for your app. Instead of sending just the score, also send a cryptographic hash (say, SHA-256 or something) of the password concatenated with the score. On the server side, recalculate the hash of password concatenated with score and check if this matches the submitted hash. If it does not, reject the score submission.
Now this is somewhat more secure than the initial approach because looking at the requests the app makes does not enable you to forge scores. However, looking in to the binary code off the app will still allow an attacker to recover the password and forge score submissions, but this is very involved and probably good enough for your purposes.
Of course, this does not rule out being able to duplicate a score (once the proper hash is known, you can submit a score as many times as you want). If you even want to prevent this, submitting a score would have to go like this: request (random) ID number from server, hash score + ID + password, submit the score + hash, the server then checks whether this hash is good. The server needs to remember the ID number so this is a little more involved to program.
A simpler solution would be to associate each score with the players name, and only allow 1 score per player. Then you could hash password + name + score.
Good afternoon!
It's better to communicate with the server via POST. So you can send an additional parameter, e.g. key, from your app to server and check it there.
The sample of PHP:
<?
$key = trim(htmlspecialchars($_POST['KEY']));
if ($key == 'your_secret_string') {
//write data to DB
}
?>
I have an Android Mobile App that is really just a calendar & you can click a certain date & a secret code pops up. The user uses that code to enter a competition - they follow the link to the competition HTML page(python script really) & enter their details to enter the competition. There are 100 minor prizes & 3 major prizes. A code can either be a non-winning code or it can win the user a prize(either the minor or major).
So they will be redirected to: http://mycompetition.com/comp.py?code=ABCDEF
Then they enter their age, code & image captcha(avoiding spammers) & click enter competition.
My Problem: I am having difficulty coming up with an algorithm to ensure that people just don't type in the above URL & put a random code value for the CGI 'code' value & accidentally win a prize if they guess a correct code(or they use a bot to keep trying).
Can you come up with any ideas to avoid someone who has not purchased the app just going to the url above & typing in a random code & accidently winning the prize?
My algorithms/ideas:
- Have the code 12 characters long which makes the probability of guessing the code very slim but still possible. I am bad with maths & probability so if I use 26 char & 10 digits as potential chars in the code does that mean the probability of guesing correct 1 out of (36 chars * 12 pass length * 103 prizes)? Does that probability leave only supercomputers(not that I believe anyones going to devote a super computer to my comp :P) able to guess the code?
- Dont associate a prize with a code. Instead just have the android app randomly generate some code that means nothing & when they enter the competition I just give them a random 1/10000 (I dont expect anywhere near 10000 entries into the comp) of winning a prize. To enter the competition you have to enter your age & the code & then enter a captcha to avoid spammers.
- Is there any easier algorithm you know of that avoids users who haven't purchased the app getting a prize?
EDIT:
- What about whenever the App is downloaded I look at their phones(wireless part) MAC address. On 1st run of the app I upload that MAC address to my server that contains a list of MAC addresses of users of my app. When/If they discover the secret code, they clikc enter competition & are redirected to http://mycompetition.com/comp.py?code=RANDOMMEANINGLESSGENERATEDCODE&uniqueID=USERSMACADDRESS. In my script I check that the uniqueID is in my list of users who downloaded my app, if it isn't I dont proceed, if it is they have 1/10000 chance of winning a prize. Can you see any flaws in this algorithm?
Use an HMAC to generate the code based on a secret you share between the Android app and the site. As the text for the HMAC, you can use a random value, which you include in the resulting code, or something unique to the user, such as their email address (meaning that each user can only have one valid code). If the length of the code is important, you can truncate the hash produced by the HMAC, but bear in mind that the shorter you truncate it, the more practical a brute-force attack is.
As long as your users cannot discover the shared secret, this will be secure insofar as an attacker would have to guess at random, or attempt to determine the secret by brute force. Since the code runs on user-owned devices, though, there's no way to prevent them from extracting the code from your app. A user with a rooted phone and a disassembler could do this relatively easily. To combat that, you could obfuscate the code, and release new versions of the app, updating the secret key there and on the site, whenever you suspect it's been compromised.
Ultimately, because the device is in the user's control, there's no way to totally prevent users from generating their own codes, but using an approach such as the one above, you can make it much more difficult for them, and easier for you to recover from it.
We are in the making of an Android app, a quiz game, which we would like to cache questions + answers for when the user is offline. The problem with this is that we would need to withhold this data from the user until she has answered the questions. Otherwise, cheating would be very easy, which would harm the game. One could then extract all questions + answers and automatically submit answers to the server API.
The most convenient way would be to retrieve a key or secret from our server at installation time, hide it "somewhere", and use it for communication with the server, and also for the encryption of questions + answers in storage. But obviously, hiding it "somewhere" is not a secure concept.
I have researched a bit, and it seems that reliable encryption is not possible in such cases, because the app has to "know" the secret (be it a certificate, passphrase + salt, or whatever) or at least where it can be found, and this can be extracted by decompilation. However, we would be fine with it being hard enough to do.
The question is: Do you know of a way to make it prohibitively hard for regular users to retrieve a secret from the APK - i.e., make it near-impossible to write an automated key-extractor?
My best guesses so far:
Hide some information in the database and use it as salt for the key
Add several bits of information from the device as salt (IMEI, email address, serial number,...) - perhaps together with obfuscation the way how the salt is applied might be hard to obtain?
You could use a separate key for each question, which depends on the answer to the previous question. So it's impossible to decrypt question 2 unless you've already answered question 1.