Related
I have an android and iPhone app, and the apps are getting data
from my website based on some conditions they select on the app.
I created a secured url that is not open to the public (can't be find on our website) and using a hash code that I thought was secure enough. Something like the following
http://test.com/data/get_data.php?key=akl;sd8234
The extra conditions they select in the app will be append as query parameters to the same url.
Base on the condition, the php file will then out data in json format.
However, I discovered recently someone else create the exact same app and actually getting data from my server, and from the secret url that I created. The reason I know is because I change something on that secret get_data.php page and it reflected on their app.
I don't know who they get a hold to my url, is there a way to create a more secure way to pass the data from my web server to the app so others can't steal my data?
You should create an API with Basic Auth or OAuth. You can't rely on your URL because it can be caught in logs.
Check this course out
In addition to shayegh's answer, you need to understand that every endpoint that you access within your application is public by nature. Everyone can access your server's endpoint just as well as your mobile application (created by you) can. However, you can make your server's endpoint protected by adding authorization requirements on your endpoint.
Authorization
There are many authorization protocols out there, OAuth, OAuth2.0, Basic Auth (credentials like email and password), etc. All of these are just ways to grant anyone access to resources in your endpoint. Think of it as a lock on your home door, only someone with a key can go into your house and make a mess.
Please keep in mind that this is a very simplified version of what authorization protocols actually look like, how it actually secures the distribution of access keys/tokens, etc.
Additional questions and answers
I tried to generate log files from my android phone, and I can't seem to find the .php file from the log files. Is that possible for someone to decode the android app or the iphone app so that they can view my source code?
Answer: I would always assume that everything that is happening on client's side (mobile app, WebApp's front end, etc) is beyond my control. That means anyone can access, read, tweak my client applications. With that said, I would assume that it is indeed possible for someone to unravel your android / iPhone app (get the source code) to get your client side keys.
How would this (OAuth/basic) help? Surely the person creating the other app would just add the OAuth/basic auth as well, wouldn't they?
In order for that other person to access the protected endpoint they would need to have access to your access tokens of the original app. On top of that, they would need to do it fast because usually access tokens only lasts for a short period of time.
Will using Basic Auth or OAuth key works if they are able to see my code?
Yes and no, depending on your implementation, attackers might be able to unravel your app and somehow fetch user's credentials or tokens from bad practices.
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.
Keep the secret in firebase database and get from it when app starts ,
It is far better than calling a web service .
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
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 .
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 .
In my Android application, the user can submit content to the database which can then be seen by all the other users as well.
This new content is sent to the server via GET request:
http://www.example.org/API.php?newContent=helloWorld
The problem is: If a user finds out what this URL looks like, he could easily sent malicious requests in his browser and circumvent the Android application. Maybe one could decompile the app and find out about the URL.
How can I protect access to this URL and prevent users from accessing this API directly?
Is it a good solution to generate a hash in the application and compare it with a hash generated in the API.php file on the server?
Couldn't one find out how the hash is generated when decompiling the application?
Thank you very much in advance!
So the only way to truly protect that URL is by requiring all requests to it be authenticated.
One way to do this is change your request to a POST request and send along some sort of auth token (a simple hash will do) with the request. If the auth token isn't present, simply don't respond to the request. The hash would be something you'd hardcode into both the client and server.
Now the question is how to hide your auth token. As long as you're not open sourcing your code, the only way for someone to get at it would be to decompile your program as you mentioned. To guard against this you might want to look into using proguard (http://developer.android.com/guide/developing/tools/proguard.html).
Something to keep in mind is that this method contains a single point of failure. If your auth token is ever exposed, you're done for (e.g. the HD DVD AACS cryptographic key debacle).
One other way to authenticate is on a per-user basis. As long as a valid user is making a request, you shouldn't really care whether or not the request is coming from the web browser or android app. I think this is a much better way of doing things. By doing this, you can throttle requests on a per-user basis. This however requires you to manage user profiles and the whole can of worm that comes along with it.
All that said, at the end of the day though you shouldn't really care if somebody knows the url to a portion of your API. I don't know your particular use case, but there's got to be a way to design your API so that you don't care how you're getting your requests. Also, if your doing a true GET, then you shouldn't be changing anything on the server. This means that all the 'malicious person' can do is get data off of it. This severely limits the damage they can do. In fact, unless you have sensitive data that you don't want certain people to look at, you don't really have a problem at all. If you do, then you should really think about my per-user authentication solution.
Don't trust the client for validation. This is true if its javascript in a web-browser or even some locked down platform like the Iphone.
If the app can make the API calls, then clearly everything needed to make those calls is on the phone ( secret, hash function, API key, whatever), then someone can always dump the phones storage and get all that data. They can then make whatever request they want.
What you want to do is authenticate the user and then validate the input on the server side.
Use SSL (HTTPS) for your data transfers. The exchange is encrypted before any data is sent, so anyone listening in won't be able to see either the URL or data that is sent to the server. To verify this for yourself, install Wireshark on your dev system and load the URL into a browser. You'll not see any data in the clear (either the URL or the data that is sent via either GET or POST).
You could use a somewhat confusing java method to obfuscate every letter of the URL. So kind of creating your own dictionary in a way which could make the URL possibly appear as 123.3*15*13 or something like that if someone did decompile the APK, they would have no idea. And on that note, you would ideally use Proguard to obfuscate it, so your obfuscation would make no sense to someone trying to reverse engineer.
You could make a short java method like this:
public String confuseString() {
Stringbuilder sb = new StringBuilder();
//your real URL would be abc.com, but in the app you have myURL = 123.3*15*13
//With what I'm saying the * would precede a 2 digit number that represents 1 letter
for (int i = 0; i < stringLength; i++){
String letter = myURL.charAt(i);
if (letter.equals("1"){
letter = a;
sb.append(letter);
} // you would go one to code each character into a letter
}
}
There would be several more if statements of course, but it would allow you to obfuscate your url without making any server side changes. And if you use Proguard, then that entire method that you create would make absolutely no sense to someone trying to reverse engineer.
You could of course make your obfuscation much more complicated than what I suggested, but it's an idea anyway.
Basically you'd be encrypting the URL in a very confusing fashion.
Here is an answer which may be a better method of encryption or at least give additional encryption:
Java - encrypt / decrypt user name and password from a configuration file