Is this a practical method for securing a Mobile API? - android

I'm building a mobile API & library for logging analytics and have been exploring different methods of securing the API to prevent unwanted 3rd parties from logging data or intercepting user data.
My proposed method is to use public-key encryption: the user would generate the keys on the API's website, and be provided with the public/private key. The user would then manually store the key within the applications code, for example, in Android it could be a string resources file. When the app is run, the library will read the key provided by the user in code, encrypt the analytics data, and send it to the server, where it is decrypted and read.
After research I've discovered several problems with this method, most obviously that a 3rd party could de-compile the application and get the stored key (somehow?). However this seems like a lot of effort simply to get the key for analytics, which would only contain anonymous information. I also remember reading that another large developer (Twitter?) used a similar method, and changed the keys between app versions.
An alternative method could be to connect the user to the server, authenticate, then distribute keys for a session. However my application has nothing to uniquely identify and authenticate individual users (no user accounts & passwords), so I'd still need to store an identity for authenticating.
I'd really like a solution thats simple and easy for API users to implement, like copying a single encryption key, and also not bloated with network traffic for authenticating, managing sessions, distributing keys, etc. I would be interested to know how existing services such as Fabric and Firebase do it.

Related

How to securely store 3rd Party API keys when back end is provided entirely by 'Back End As A Service'?

I am trying to develop a strategy for securing third party API keys for a React Native app. When building a web application, API keys can be stored in environment variables and then secured when deploying the application server.
However with a mobile app that only uses 'back end as a service' back end services, like Firebase, the app is not deployed, it is distributed and services are accessed.
After doing some research, it seems there is no standard / straight forward approach to solving this problem although it must be a situation that thousands of app developers find themselves in.
I am using Firebase, but also two other services that require API keys. My initial solution was to add these API keys (not Firebase keys) to Firebase environment variables as described here and then make a call in the app to a Firebase Cloud Function and to retrieve them. But I think the problem might be here that the keys can still be intercepted as they will be retrieved in plain text.
Is this a good approach to storing the third party keys, and if not, what other approaches could be taken?

Best practices for API Key and Secret in bundled in App

I'm developing an app that will use text messages to verify a user's telephone number, the usual "enter code" routine.
After reading a little bit it seems like a bad idea to store the private keys for whatever 3rd party I'll use in the app (twilio, nexmo, etc). Somebody could reverse engineer these from my binary and use them in their app.
However, having these on the server doesn't help either, somebody could just reverse engineer my server's endpoint that I use to send text messages and use that instead.
E.g. I could reverse engineer WhatsApp and get the private keys or API endpoints that they use for telephone number verification and just use that in my app, saving me thousand of dollars.
Any ideas on how to protect myself against such an attack?
Hiding API Keys on the server
However, having these on the server doesn't help either, somebody
could just reverse engineer my server's endpoint that I use to send
text messages and use that instead.
Yes it does help a lot.
If somebody gets access to the keys to your web service, they can only do, what your service allows them to do. This is a very good idea to have a web service that encapsulates all the 3d party keys and API - it's way more secure.
Nobody will ever get access to your sensitive keys, that'll allow them to do everything.
For example the 3rd party API allows deleting - your server wrapper API will not allow it.
Moreover, you can add any extra logic or alerts for suspicious behavior.
Hiding API Keys in the app
If somebody sets their mind to it, there's no way you can prevent getting your keys reverse engineered from your app. You can only make that harder. Computer security should never be about "how hard/complicated it is to do", but in this case we have no choice.
Ok, so you have to hardcode the API keys into your source files. It can be easily reverse-engineered.
You can obfuscate your keys, so that they can't be read directly. The result will be that they'll be scattered in a compiled file, rather than comfortably being placed in one place.
On iOS you can use something like this.
On Android you can use DexGuard, or any other way to obfuscate a string.
Encrypting the keys
Another layer of making it hard for hackers is to encrypt the keys.
Here's an example for iOS.
You can do the same for Android.
Perfect Scenario
Ok, so let's say you have a 3rd party API for video management.
The hacker wants to delete all videos on the server, because the 3rd API allows that.
First he has to glue up all the scattered strings in the file. If he manages to do that, he has to find a way to decrypt that.
Even if he manages to decrypt that, that'll give him the API keys to your server and your server and your server only allows to upload videos, not delete them.
I think firebase functions can help us in hiding the third party API keys.
The proposed solution-
Store API keys in firebase as environment variables.
Make a firebase https function that answers to only the authenticated users. If an authenticated user requests it, the secret API key from the firebase environment variable is returned as the response.
Android app does an anonymous login into firebase for the first time, obtains the token.
This token is used as Authorization token in headers while requesting firebase https function. The firebase function would be something like https://us-central1-{your_project_name}.net/{function_name}
I have discussed the approach in detail in this blog and made a sample project

What is the best place to store secret keys in an Android app?

What is the best place to store API keys, Database encryption keys etc. in the app code so that nobody can get it by decompiling the code ? I used proguard to obfuscate the code but it didn't work on Strings.
There is no way to store them in the app. The app can be decompiled or executed on a modified device which provides more access to the app's memory, or the app can be modified by the attacker to add additional logging of network or storage/database traffic, etc.
For authenticating to servers, your app should probably obtain auth tokens (or similar) by exchanging user-entered credentials for such auth tokens or by obtaining these auth tokens from AccountManager or similar APIs. You could also use SafetyNet Attest API (https://developer.android.com/training/safetynet/index.html) to attest to your servers that it is your app signed with your signing key which is making the request.
For database encryption, the app could generate a random encryption key on-device, either linked to user-entered credentials or stored in Android Keystore, or simply rely on protections offered by Android to apps. It depends on your threat model (i.e., why do you think you need to encrypt databases?)

End-to-end symmetric encryption from Android app to Angular.JS Web app and back

I'm building an Android app that contains sensitive chat messages.
I'd really appreciate some help regarding an encryption workflow that allows me to encrypt these messages, store them in a remote database, query for them via Angular.JS and finally decrypt them and present them to the user.
The server must not be able to decrypt the messages. Only both Android and Angular.JS clients should be able to encrypt and decrypt the data, and the encryption key should be unique for each of my users. Both clients can send messages, so both need the ability to encrypt and decrypt.
Is there any way to get this done without requiring the user to enter a custom "Encryption Key" in both clients? Is there any way for this to be automatic in some way, and without involving the server? If not, what are the best-practices in this condition? I wasn't able to find any example of this kind of encryption in any wide-known service as of today.
Thanks!
You're asking about how to do key exchange without revealing the key to the network, right?
Diffie-Hellman key exchange is one well known algorithm for doing this. The important high level properties are that the two parties, in the end, agree on a shared secret that a passive eavesdropper can't get. However, the parties don't authenticate each other, so they can't tell if they're running the algorithm with a man in the middle (e.g., the server in your question).
I've seen products use password-authenticated key exchange. As the name suggests, these algorithms require that both parties (in this case, the same user, but on different devices) know a password. So ultimately, going with this approach requires the user to enter a password on one of the devices (the other can generate it and display it to the user). It's a little less troublesome than entering an entire encryption key into both devices, right?
As for technical implementations, it's still probably going to involve the server (or a server, if not the database server) just to relay messages, but these key exchange algorithms should keep the shared secret confidential.
I wasn't able to find any example of this kind of encryption in any wide-known service as of today.
One great resource I've found is a page from Mozilla's wiki on how they implemented key exchange in their Firefox Sync product. They use this when you set up Sync on multiple devices, which requires the second device to get the key from the first device.

What is the preferred way of using AWS (specifically S3) from mobile apps?

Adding the AWS access key and secret key directly in app code is definitely not a good approach, primarily because the app resides on the users device (unlike server side code), and can be reverse engineered to get the credentials, which can then be misused.
Though I find this information everywhere, but am unable to find a definitive solution to this problem. What are my options? I read about the token vending machine architecture for temporary credentials, but I am not convinced that it is any better. If I can reverse engineer the secret key, then I can reverse engineer the code which requests for temporary credentials. And once I have a set of temporary credentials to access S3, I am as good as if I had the key. I can request the temporary credentials again and again, even if they expire pretty quickly. To summarize, if an app can do something, I can do the same as a malicious user. If anything, the TVM can be a bit better at management (rotating credentials, and changing key in case of breach, etc.). Please note we can put the same access restrictions on the secret key, as we plan to do in case of TVM temporary credentials.
Additionally, if Amazon doesn't want people to use the secret key directly in the App, why don't they block it in their SDK, and enforce TVM or the correct solution. If you will leave a path, people are going to use it. I read several articles like these, and wonder why?: http://blog.rajbala.com/post/81038397871/amazon-is-downloading-apps-from-google-play-and
I am primarily from web background, so my understanding of this may be a bit flawed. Please help me understand if this is better, and whether there is a perfect (or may be good) solution available to this problem.
PS: Is there a rails implementation of TVM?
Embedding S3 keys in App code is very risky. Anyone can easily get that key from your app code (no reverse engineering or high skill set required), even if that is stored encrypted it is still compromised just that someone need to try harder (depending on how do you encrypt).
I hope that you understand the advantages of using temporary credentials to access Amazon (S3 etc) resources (mainly security + some others like no app update etc). I think you are more confused about the process to get the temporary credentials from TVM and how that is safer than embedding keys in code.
Every client using TVM first need to register with the TVM server implementation hosted by you. The communication between App (using TVM client) and TVM server is over SSL.
First the app register with TVM by providing UUID and a secret key. Please note that the secret key is not embedded in App code (which I think is main reason for your confusion) but generated randomly (using SecRandomCopyBytes which generates an array of cryptographically secure random bytes) at the time of registration (and hex encoded).
Once the device is registered successfully with TVM, the client TVM store the generated UDID and secret key in a storage called Keychain in iOS and Shared Preferences in Android. The keychain in iOS is the shared storage provided by iOS to securely (encrypted) store information (mainly keys, password etc).
After registration and UDID/Secret Key storage, App can get the token from TVM by sending the UDID, cryptographic signature, and a timestamp. The cryptographic signature is an HMAC hash generated from the timestamp using the secret key. The TVM can use the UDID to lookup the secret key and uses it to verify the signature. The TVM then responds by sending back temporary credentials, which are encrypted using the secret key (uses AES). The application decrypts the temporary credentials using the key and can then use them to access any AWS services for which the temporary credentials are authorized. Eventually, the expiration time of these temporary credentials will be reached, at which point the application can get the fresh temporary credentials, if required.
I am not sure how signed URLs relate to TVM, because I don't understand the concepts 100% but signed URLs really solved the problem for me. I needed a mechanism that would feed web app and mobile app data without allowing for misuse of the credentials. Putting the key in the code is indeed a very bad idea as it may generate a huge bill for the company.
After 3 days of extensive research, I found a simple and, what seems to be, a reliable and relatively safe solution: signed URLs. The idea is, that a very light-weight back-end can generate a temporary URL that will grant the user access to the specific resource for a limited time. So the idea is simple:
the user asks our back-end with a Rest call that he wants a specific resource
the back-end is already authorized with AWS S3
the back-end generates a temporary URL for the user and sends it in the Rest response
the user uses the URL to fetch the data directly from the AWS
A plug-and-play Python implementation can be found here and with a slight modification that I had to use: here.
Of course one more thing to figure out would be how do we authorize the user before we know that we can grant it the URL but that's another pair of shoes.
You should ideally use Cognito Identity for achieving this along with appropriate policies. It should be used with S3TransferUtility and S3TransferManager in iOS and Android SDKs. That would allow for background uploads and downloads as well. Cognito vends temporary credentials for access to AWS resources and is free. Also, you could federate it using UserPools or providers like Google, Facebook if you want secure access.
Thanks,
Rohan

Categories

Resources