When a user logs in from my app, I receive a user-id from the API on a successful login. That ID is then used throughout the app to interact with the API. Currently, I am storing that ID in shared preferences. However, a user with root access can easily change the ID, thus identifying as someone else they are not.
How should I store this id without causing security risk? If this isn't possible what are other alternatives to this?
It sounds like the problem is fundamentally that your app is trusting the user to tell the app who they are via this user ID. What your app should most likely be returning, and passing back to the app for API use, is a cryptographically based session token that the app knows how to unpack to determine the user's identity, but the user has no way of altering without invalidating it.
Related
In my Android app, I am getting the user information from server using a HttpURLConnection. Each user has a unique Id which may be accessed publicly.
Problem is if a third party, say, UserB has the Id of UserA, then they can abuse it by setting it inside my app (we know that how easy is to decompile Android APKs) then calling the server using my app and getting the output from server (including sensitive private information of UserA).
How can I prevent this from happening? What security tricks do you recommend?
Side note 1: I already have used encryption/decryption methods. But they are not going to stop UserB from abuse because when the UserB sets the Id of UserA, the app calls the server and then they have access to final decrypted output.
Side note 2: I have thought about Phone Number verification, but it is not operational for my app currently.
Side note 3: I cannot restrict the user to a specific device because they have to be able to use it on their different devices.
Side note 4: Libraries like Firebase, Gcm and the like are so secure. It would be a good idea to find out what ways they use to keep hackers from accessing another user's information!
Side note 5: Thanks to Gabor, I noticed that I had to mention that I cannot use a login interface unfortunately. If I could, that would be for sure a primary choice. It's the nature of my app and I can't change it!
That's actually a very good question and a holy grail of all security officers :)
What's I learned is that whatever you do, as long as you cannot protect the physical device against unauthorized access, there's always a risk. The problem is:
* On the one hand, you wish that the app will keep and store an information which authorizes the owner of the device
* On the other hand, you have to protect this information on the device, but because your app needs to be able for accessing it, also the accessing algorithm must be available on the device.
So I would say, there's no "clear" way. What you can do, is to make the cost of obtaining information (in your case) unprofitable.
You said that the user ID is "publicly available". That's ok, but it means that the UserID is not "secure information". I would say, that your users need to be authorized first, and then the server should generate a UNIQUE token, that will be used in replacement of UserID when calling next requests.
You can implement it in many ways, but I will recommend one way, that should be not complex for your users:
Let's assume that all your users are registered. During the registration, each user needs to provide his email, UserID (could be generated) and password. Note that registration could be done in the mobile app or on the web portal.
When the app is going to start for the first time, a user should provide hist UserID (or simply email) and password.
With the first call to the server, the userID and password should be sent to the server, and server generated a Token (unique for every first login so even if the user will use two or more mobile devices each one will use the new token).
With every next call to the server, only the token needs to be provided to authenticate the user/device.
Where to store that token? No matter. I would say in any private storage of the app. You can and should, of course, encrypt it, obfuscate a so one, but whatever you do, if one has access to the device, he can always copy it.
You will say now, that it's not 100% secure. That's right. So I would say if it could not be secure, we should minimize the risk of abuse.
There are also some ways how one can do that.
First of all, in case of abuse, your users should be informed about it. Having a token algorithm, the user can take and action, and simply disable stolen tokens.
In case if the device has been for instance stolen, your users can/should be able to disable tokens (devices) on the web portal (or in another instance of the app) after signing in using email/password authentication.
The only one problem is how to detect that the mobile device has been "cloned". In that case, the user is physically not aware of the abuse.
My guess here is to implement the following algorithm (auth pooling):
1. Let the mobile app send the "keep alive" message with the Token to the server at the certain time when the app is inactive (let's say user xyz#gmail.com should send keepalive always at 10:00, 12:00 and so one).
2. Let the app send the keep alive, with some frequency when the user is logged in (app is active).
3. These frequencies/schedules must be know for the server and app (and could be even public).
In case if the server detects the same token in keep alive nearly the same time, the user should be informed (by the different channel, it could email) about possible abuse.
* NOTE: this is only an idea, I never did that, and I'm also wondering what other things about it, but in my opinion, this is quite simple to implement, and gives you a good change to minimize the risk.
When a user starts using the app, they should log in using their credentials (eg. username and password). From the app's perspective, this is a roundtrip to the server to obtain a token. The token is then stored in the appropriate credential store for the platform you are using, and can be used to impersonate the user in further requests (the token can be sent with requests as for example an Authorize header, practically a bearer token). It also should have an expiry time, after which the user has to provide his credentials again.
Such a token can be a plain jwt you create, or it may come from something like a full oauth2 / openid connect implementation. If you decide to implement it yourself, be careful, it is not straightforward to get it right.
This way, you have proper authentication in place, you know who your users are in subsequent requests, and this way you can enforce access control rules on the server.
I'm trying to work out the best solution for a particular situation I'm in, and am having trouble working out the best option. It's a tricky setup, so might be a fun challenge for you Android experts! Here's my situation:
We have two Android apps already on the Play Store, and are working on another right now.
The two released apps are signed with the same keystore, but do not currently have a sharedUserId set in their manifest files.
The two released apps store a user's oAuth token in SharedPrefs, some product/content data in an SQLite DB, and some audio/video content in external storage (using getExternalFilesDir).
The apps are all separate oAuth clients/applications of our server (i.e. they all use different client ID and secret keys).
Our server is setup to only allow one oAuth token per oAuth application (i.e. Android app). e.g. if a user logs in to app A on one device, then logs into app A on another device, the first device's token will be invalidated and the app will receive a 401.
We've successfully implemented a single sign-on system on iOS by enabling shared keychain between the apps. If an app detects that another app in the group has a valid oAuth token, it can send that to our server and exchange it for a valid token for that app.
In the iOS version, we wanted to ensure that the apps didn't need to know about the existence of the other apps using hardcoded values per-app (e.g. if we release a new app in the future, other apps don't need to be updated to share/receive tokens with it), so we created an entry in the keychain containing an array of bundle IDs that had valid tokens, that all apps could access. When an app successfully logs in or exchanges a token, they add their own bundle ID to that array. Any newly installed app could find a bundle ID from that array, and use that it as a key for loading the token details for that app, which it would then exchange for its own fresh token.
We want the token exchange to be automatic and not require input from the user.
I hope that all makes sense!!! Please let me know if not.
I'm now trying to work out the best method for storing oAuth tokens (plus some additional data such as email address to go with it) on Android so that other apps owned by our account can access them in order to exchange for a fresh token.
I've looked into using the following, but am unsure of the best route:
SharedPreferences along with sharedUserId
AccountManager (https://developer.android.com/reference/android/accounts/AccountManager.html)
Android Keychain (https://developer.android.com/reference/android/security/KeyChain.html)
ContentProviders
The problem with option 1. seems to be that setting the sharedUserId after first release will lose access to all of the data (see http://java-hamster.blogspot.jp/2010/05/androids-shareduserid.html). This is not a nice thing for our users.
Option 2. (AccountManager) could be a good option, but if we want to store the tokens separately (per-app), but want any other apps we make to access their tokens, I'm not sure how we'd do that.
Option 3... is it possible to do what we need with Keychain?
If I understand correctly, option 4 would need each app to have its own ContentProvider? I'm not sure how that would work for our requirements.
If anyone has gone through this kind of situation and could share some insights and recommendations, I'd really appreciate it!
ContentProvider is probably your best best. I won't be able to provide the whole code for you to do this, but this is generally how I see it working:
Each app creates a content provider that exposes their own API token;
Each app tries to acquire and query (sequentially until successful) all the other content providers before requesting a login;
If app X is able to acquire and retrieve the token from app Y (via Y's content provider), then store it in app X and use it;
Side notes:
This is very sensitive information, so you should enforce security. Your content providers should only be accessed by other apps signed with the same key, they should have only read permissions and you should create your own custom permission as well.
The content provider does not need to access an sqlite database. It can access whatever your using to store the token (which I hope is stored in a secure way, but I'll leave that to your own judgment)
Useful links:
How to create a custom content provider
How to secure your content provider
Example of content provider that retrieves data from shared preferences;
AccountManager is meant to solve the exact problem that your are commenting. Here is a good tutorial to work with it:
http://blog.udinic.com/2013/04/24/write-your-own-android-authenticator/
Just be sure that you sign all your apps with the same keystore, because this is the only thing that might complicate things (a lot).
Using preferences will lead you into a problem as all that you store in the main thread is not guaranteed to be there in a Service (like a SyncAdapter). There used to be a trick for this in the form of flag (MULTI_SERVICE) but was deprecated in api 23.
ContentProvider is of course possible (its too generic) but AccountManager will help you to cover the corner cases related from refreshing tokens and other interesting stuff.
How can you uniquely identify a user who has installed your app so that:
You will know it is them if they delete and reinstall your app;
You will know it is them if they install your app on a second device they intend to use simultaneously?
Just as an example, I see that the Netflix app will automatically link to your desktop account without any user interaction. I'm guessing that they use accountManager.getAccounts() or similar method, because they also require the GET_ACCOUNTS permission. But of course that permission is marked as Protection level: dangerous. Is there any technique to do this that is less invasive or potentially alarming?
The key to answering this is to be both simple (for the user) and minimally invasive. Android provides heaps of ways to identify users and many of those ways involve piercing a user's privacy, and if that is the only way, I will do what I do now (optional email registration). I just want a way for my app to know if a user already is registered in my system across installs without having to interview the user (username/password, email address, third-party OAuth, etc).
My main reasons are:
I don't want support requests from users who orphaned their content after a reinstall; and
I don't want to host lots of orphaned content.
Have a look at Firebase Authentication. It's quite seamless and does not require much effort to incorporate. Also it does not feel intrusive or cumbersome to the end user.
Here is a video tutorial by Google.
EDIT:
In case your users are sure to have a cellular device with a phone number, you can use AccountKit. It is also what they call OTA (One Time Authentication). AccountKit uses just the users phone number to verify and validate users.
EDIT:
Firebase Authentication now features 'Phone Verification' which is similar to AccountKit mentioned above. Both are good services. However, Firebase phone verification lets you make your own UI from scratch (which means a lot better control than AccountKit). Also, if you don't want to make your UI, you can always use FirebaseUI
i have implemented something that seems little similar to your thing by push notification , i can get error if user uninstalled my app(and from the registration id i get the user) , and if he re installed he obtain a new registration id , and try to get the user UUID for different devices
I think the simplest way would be using UUID and storing the hash on sharedPreferences. You should generate the UUID as earlier as possible in your app.
sharedPrefs = context.getSharedPreferences(APP_SHARED_PREFS,Activity.MODE_PRIVATE);
if (sharedPrefs.getString("YOUR-KEY-TO-THE-UUID") == null || "".equals(sharedPrefs.getString("YOUR-KEY-TO-THE-UUID"))){
prefsEditor = sharedPrefs.edit();
prefsEditor.putString("YOUR-KEY-TO-THE-UUID", UUID.randomUUID().toString());
prefsEditor.commit();
}
I think that the best way would be implementing login with Google or Facebook. This is quite seamless for users, safe enough (as Google and Facebook considered trusted), you do not need to implement your email registration and you will have identity across devices.
If your app is Android only and you'd like to provide identity without any account creation for the user, I believe using Google Account name/id is the best choice (Accessing Google Account Id /username via Android) since you have to use Google Account on Android phone (unless you root it, delete Google Play Services etc).
If you'd like to only address the first point of your question (identify after reinstall) there's a Device Id -Secure.getString(getContext().getContentResolver(), Secure.ANDROID_ID);
though it's not 100% reliable (f.e Factory Reset resets this value)
The standard for achieving this sort of functionality is through the use of JSON web tokens (JWT) in conjunction with standard restful api traffic.
Assuming your android application interacts with a RESTful api for all crudlike operations and business logic, then using a JWT as an authentication identifier to your api can work quite well. You can embed information in each JWT allowing you to identify whatever you like (the user id in the database, the device id of whereve the user logged in from, etc). A JWT is essentially a datastructure allowing you to store information to be used by the API.
Some basics for how this works:
Getting the JWT into the app: A user logs in to the application using their username/password. The
api then returns an encrypted JWT to be used by the client for all future requests. Don't try to do
the encryption yourself. Any language that can handle serving an api
will have libraries for this.
Using information in the JWT: The JWT is itself a datastructure. For example, it might look like this:
{
user_id: 1,
device_id: 44215,
device_os: android,
}
Your api will decrypt the JWT when it is supplied for
authentication via the request header, and then have that information available in the
context of the session.
If you provide the language used by your api then I might be able to recommend a library.
I will conclude by referring to the final requirement you submitted which states essentially that you do not want to have to interview the user across installs. If I understand your meaning, that you want a user to be able to simply install the application and begin using it without supplying authentication credentials, then there is no way to achieve that securely. You might be able to come up with a hackish way to get it to work, but it will be fundamentally insecure.
What is the best and safest way to save user credentials on device for an android app.
I was thinking of encrypting data and saving it in preferences.
Is there a way similar to iOS keychain to save passwords?
The best idea is not to save them at all. Encrypting provides very little security in this case because the app itself has to have the decryption key, so the key and data are on the same device. It will prevent only the least determined attackers. An OS level device isn't much better, as anyone with physical access can easily get around it.
The best idea is to use an access token. Get the login data once, send it to the server to login, and have them respond with an id. Use that id in future requests to identify yourself. The server should remember who is associated with each id. Preferably the server will include a timeout mechanism, where after X amount of time the id will be invalidated and the user will need to log in again. Even more secure implementations will match it to some physical id of the device as well, such as the Android device id, requiring attackers to have the device or fake both pieces of information.
The use of an access token rather than saving credentials protects the users in a few ways. First, the attacker will not know the users password in case its reused for other services (like their email). Second, it will not be enough to change their password (because a secure service will ask for the password again to change it) so while the info in the account may be compromised the user can take back the account by using their password to change their password. If the actual password is saved and lost the attacker can change the login info and lock the user out of his account permanently.
All sorts of mobile apps - Gmail, Facebook, Pandora - have some persistent mechanism of authentication that enables a user to set up credentials once and then use them to automatically authenticate with their remote service in the future. I'm probably blind, but I can't seem to find a tutorial anywhere out there that explains in simple terms how to properly do this on a mobile app.
How do I build this functionality? A link to a simple tutorial would be great.
As Deva said, SharedPreferences is a perfect quick and easy solution for creating this feature. Usually when I want to implement this I follow this simple flow:
Logging In:
When the user logs in save the user id (it can really be any unique identifier) into Shared Preferences. This information should now be available so that your app can recall it later
Rebooting:
When the app reboots it should check to see if any user id is saved in Shared Preferences. If not, then there is no one to automatically log in. If there is, then reload the user information using the user id from the server or whatever.
Logging Out:
When the user logs out make sure you delete the key/value pair from Shared Preferences.
For this probabely you can try a SharedPrefrence. The first time user enters his details , the values get stored locally if the user is authenticated and every consecutive time you can check the same prefrence if the value is already there directly pick the value and invoke the service for authentication.
Apple's KeyChain is service ment exactly for such a scenario. it enables a persistent, secure and easy to use storage.
Good tutorial (+ demo application) here