I've been reading a lot of threads and decided to to post my conclusion before going on coding. I've found a lot of interesting things (What is the most appropriate way to store user settings in Android application) and this is what I have gathered so far :
We'll suppose that https is always used.
By "remember me", I mean the following : the user will never authenticate on the app ever again because it would annoy him to authenticate even once a week.
When not using a "remember me" feature : Oauth2 is the way to go, exchange tokens are used -> nothing gets stored, most secure
When using a "remember me" feature :
Upon first Register/user/login usage of the app :
Password is hashed by server with its own "private key"/hash and returned to android.
This hashed password is then encrypted before being stored inside SharedPreferences. Given the Hashed password never expires, we now have the following caveats :
If the phone is lost/rooted, only this hash can be retrieved to access user's data on the server : user's data is compromised.
The crypting key is stored inside code which can be decompiled : : user's data is also compromised. Since attacker can uncrypt the hashed password and use it.
Conclusion :
Using a "remember me" feature, while handy for the user, makes it vulnerable.
My question (at last :) )
Is this conclusion correct ? Did I forget an obvious solution ?
I cannot find any safer solution given the constraints (no expiry, use remember me feature)
Thank you for your help !
In principle you are correct, I will add just some technical notes.
https - for higher security use certificate pinning, your .apk should contain either certificates of allowed CAs, or signature of the server certificate, or public key to compare with server certificate (this prevents from man-in-the-middle SSL attack).
On server side store passwords as "strong_hash(salted(password))". Don't ever store plaintext password, or hashed-only. Whether salt is fixed, or generated per user (mangling user name into fixed salt), or per registration (you will be unable to login from different device, which doesn't know the proper salt), that's your choice.
About login (in case the "remember me" token expires): use always challenge-response way, so client will send his challenge to server, server will send his challenge to client, then client will send strong_hash(client_challenge+server_challenge+strong_hash(salted(password))), which server can generate from the stored strong_hash(salted(password)) too, and compare that (sending back "remember me" token, stored both on server/client side in encrypted form, with meta data about date of creation, etc.). If somebody is eavesdropping the communication, the hash sent will be valid only for those challenge values, next try to login on the same user account with different challenges would make it invalid.
If the security of the app data are really vital (like mobile banking), you can beef up the bad security of "remember me" to semi-secure level by encrypting the full "remember me" token by some simple "PIN", requiring the user to enter the PIN every time he runs the app. This makes it impossible to break in by hand (5-8 wrong PINs will delete the token and block the app, requiring full login with challenge+password, or even resetting the device by other channel (phone call, or web app) on the server), and brute-forcing it programmatically can trigger some alert on server API side of suspicious rate of failures for particular user. Yet entering 4+ digit PIN is not as cumbersome as entering full password, and it's usually well accepted by users in applications with high security requirements.
Otherwise yes, "remember me" can be stolen, it's just question how easy you make it, if the device has some tamper-proof keystore unlocked by the user presence, use that one instead of shared prefs, or at least encrypt the values with some key (fixed + generated-per-device part?). But it's just piling up stones on the path, somebody enough determined and skilled can extract the token anyway, and use it on other device to mimic the original user.
I'm not sure what your question is, but I'll use this space to clarify some stuff.
Your findings are correct: "remember the password" (even hashed) locally on device is a bad idea, should not be coded by anyone and users should never use an app that does it.
In reality even on the server side passwords should not be technically possible to be unencrypted. Server side password hashing must always use a 1 way encryption.
You never ever ever ever ever store password locally. Ever! You always store an oauth key locally. Hashed would be nicely. Even better if on the server side that Oauth key is tied to some sort of device ID, meaning that Oauth con only be used with that device.
What makes you think users have to "login again" when using Oauth? The keys don't have to expire (the server might have some expiration mechanism), and even if they do, there're key exchange techinques for that.
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 need to store user credentials in my Android app. The application sends HTTP requests to a server and the credentials are used to authenticate with that server.
I know that many articles and many discussions about this topic already exist. However, I would like to ask for help considering the following specific requirements:
The user should be asked to type username and password just once. After that, the application must be able to authenticate with the server forever.
If the server receives an authentication request that contains username and password, it generates a random token and sends it back to the client.
The client must add the token to other requests (as an HTTP header).
The validity of the token always expires an hour after it was generated. The client must send another authentication request with username and password to get a new token.
Note that the server provides a custom authentication mechanism that does not follow any standard.
I would say that these are very strong requirements and that they already impact some security issues. However, let's suppose that these conditions cannot be changed and that they must be met by the Android app.
Here is what I am going to do:
As the user should provide credentials just once per app installation and as the token on the server side has a limited validity and the client must re-authenticate with the credentials again and again, the Android app cannot avoid storing the password.
I will store the credentials into DB (by subclassing SQLiteOpenHelper from the android.database.sqlite package).
I will encrypt the credentials before storing them but the key used for encryption/decryption will be just a constant hardcoded in the app.
Additionally I will set android:allowBackup to false in the manifest file and obfuscate the application.
I know that an attacker with physical access to the device can get the credentials. I am also aware that some of the suggested steps are just little obstacles for such attacker.
However, is there something more I can do to improve security if there are the requirements mentioned at the beginning of the question?
Thanks.
If I were in your shoes, I would use SQLCipher to store the user credentials. This is a simple way to create and use an encrypted (using AES) sqlite database with minimal hassle.
Of course, this doesn't solve the whole problem. You still need a secure way of generating/storing the key to said database. If I were to recommend any option, I would advise requiring users to input a password/PIN whenever they open the app, and use said password/PIN as the database key.
An alternate method would be to generate a unique, random key upon app installation, and store it in the Android Keystore. A truly dedicated/well-funded attacker would still be able to retrieve the key, but only for the database on that one device.
I've been mostly creating smaller apps and games for Android so far, but am now creating a somewhat big app with lots of users and more sensible data than a highscore.
My normal approach was to just have a table for all users with passwords, authenticate with a simple Login Screen using a HTTP(S) call and that's it.
There's a few things I want to improve for this app though:
Secure Transmission
If I want to encrypt the user's password, where do I need to do it? On the device, before it's even sent? (In case of unsecure networks, like a public WiFi hotspot) Or better on the server, before writing it into the DB? Or should I just use SQL's encryption?
Auto Login
I want users to be able to stay logged in until the log out - how would I best do that? Not just security-wise, but also for the user experience.
My research shows me that using the AccountManager would be best to save the username and password and authenticate automatically when the app is started. Is there anything more to it, any security risks I'm missing here?
Access control
Usually, I would just expect every call made by an app to be valid, since a user can't access anything but the login screen without logging in. But how do I best authenticate a user's request to make sure that it's not an attacker? I can't just send the username/id with every request, so I probably need like a session token that I generate on each login? Or is there a better method?
Is there anything else I've forgot to think about?
I would suggest you to transfer password without encrypting it but by https. Other way would be to implement asymmetric encryption in your app and encrypt password with public key which you will receive from server.
On the server side I would hash password using some hashing algorithm with salt. And store only hash and salt. When users will log in, you can hash incoming passwords the same way and check hashes on equality.
To make auto login, you need to sign all requests from authorized users with a token. Token you will receive from the server after successful login. This token could be stored in Keystore, or special storage which is accessible only for this application.
Signing could be implemented by attaching to request additional parameter with checksum from all request parameters and token.
Additionally I would suggest you to think about unauthorized clone apps, which could pretend to be your app and call your server side API.
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.
This is a messaging app. It has no login (username or password). This means it has to send messsages to a server, and the server must trust it is coming from the phone number it says it is coming from.
How do you do that?
1) Send token to phones with text message service
Unfortunately, you're not going to be able to guarantee with 100% certainty that the phone number reported to the server is the one it's coming from. The reason is that the client code can be reverse engineered, regardless of what you do. There are some things you can do however to make it a little more difficult, although it is important that you don't depend on this to be 100% secure. If you need 100% assurance, then you need to have the app authenticate to the server through traditional means.
The way that I would do this, is embed a token in each copy of the client that gets released, such that each client has a different token. The token should have extremely large entropy (such as a 128-bit or greater integer), and you should keep a list on the server of tokens you've issued with the phone number of the device, so you can check them for validity. Require re-installs of the app to use a new token, and blacklist the previous token so it can no longer be used. The UID can be used, but be advised that it can easily be spoofed by a rooted device.
Submit this token to the server each time and ensure that the phone number the app claims to have never changes. To make it harder for reverse engineers to find the token in your code, you can xor it one or more times with additional tokens, and you can also lay out a bunch of fake tokens throughout the code that are blacklisted on the server.
You can also encrypt it with a secret key that is retrieved from the server so that the embedded token must be retrieved by an RE during live interaction with the server. Again this in no way guarantees that the token won't be found and changed/stolen, but it raises the bar for potential reverse engineers.
To prevent someone from sniffing the wire and obtaining and/or tampering with your token, or from using a proxy like Burp Suite to capture/tamper with it, you should use an encrypted HMAC. If you've never used an HMAC before, be advised that unless you encrypt it, it only provides authentication and integrity, not confidentiality.
EDIT:
Should also add, that you should run your code through an Obfuscator before you deploy it. This won't obfuscate the token, but it will obfuscate the decompiled code so it looks like gibberish to the RE. This forces the RE to use the byte code/assembly code from your app, which is much, much harder.
Associate each phone number with a unique device identifier
Get the udid and send it with each request
Create a signature that follow each request to your server. The signature shoukd be something like: secret key1+msg+phone number+udid+secret key2, then SHA1 the string and attach to th request.
On server calculate the signature and compare to original that followed the message. If they match, ok, else don't send it.
Use strong keys, and use two, to make brue force extraction, almost impossible.