Storing a password safely is a difficult issue, but Android provides a robust set of APIs and a secure container to help with this. As an experienced mobile developer and previous employee of a mobile security company, I thought it would be beneficial to express my ideas. Read on to find out How to Store Passwords Securely on Android.
Things to Consider
Why store passwords in the first place?
The best approach to protect user credentials is to never keep them. The user can enter the credentials when logging in, and you should utilize them for authentication, such as against a server, before discarding them from the UI and memory. It is an important factor to consider in your security design and should be the preferable option.
Keeping password hashes safe
If there is no other option but to save a password, such as if you are not saving the user password but another credential that you are attempting to safeguard, it is always a good idea to store a derivation of the password that may be used for authentication, such as a password hash.
A hash is a sequence of characters created from the original by using a cryptographic function such as SHA, which is designed to make re-engineering the original password hard.
Adding some salt
So you’re storing a password hash, and a hacker can’t reverse-hash the original password, right? Regrettably, no. A skilled hacker can determine which hash algorithm was used to generate the hash based on its composition and length. Then he can begin a brute force attack by iterating over a list of well-known passwords, applying the hash function, and comparing the output to the user’s initial hash. This might be a highly automated procedure capable of checking hundreds of passwords per second.
A ‘Salt’ parameter can be added to the hash function to prevent brute force attacks. A Salt is a hash function input parameter that is used by the hashing process to randomize the hash output. A hacker cannot infer a relationship between a password and the produced hash without knowing the Salt.
Enforcing complicated password policies
If your app does not enforce complex password constraints, you might just skip the bother of securely storing the password. Allowing passwords that are easy to guess (uhm, Password123 comes to mind) or are in the dictionary increases the likelihood that someone would hack in.
We installed a new virtual machine at our workplace a few months ago. I recently checked in and discovered that there had been over 250,000 attempts to connect to the server in just a few months. It’s surprising, but we must recognize that we are actively under attack by bots and hackers that make a living by breaking into your program.
What exactly are secure password rules? It truly relies on a lot of things, including the level of security of the data you’re seeking to secure and your user base’s tolerance for selecting, remembering, and typing in difficult passwords. Here’s a standard recommendation:
- Passwords should be at least 12 characters long.
- A minimum of one capital and one minuscule letter, as well as one number
- At least one unique character
- The password should not be found in a simple dictionary once it has been stripped of digits and special characters.
Limiting password retries
Allowing unlimited password retries exposes your app to brute-force assaults. In order to gain access, a hacker can write an automated script that attempts the most common passwords. A list of popular passwords has been extensively distributed. Wikipedia even has a list of common passwords.
The solution is to limit the number of password attempts a person can make, say, to five and then lock the account. Following that, it will ask a user to supply additional security credentials, such as answers to security questions, in order to unlock the account.
Read Also: How To Check Apk For Virus? – Full Guide
When sending credentials over the network to a server for authentication, it is critical that the network communication between the app and server is encrypted with a robust encryption method. Otherwise, a third party listening in on network traffic (a man-in-the-middle) may readily harvest credentials from it.
The easiest approach to accomplish this is to utilize 2048- or 4096-bit keys for SSL communication and to lock down any non-secure communication to the server by installing a certificate from a well-known CA on your web server.
Checking for device security
It may be a good idea to guide the user in securing the device itself in order to protect access to your app. The Android API KeyguardManager.isDeviceSecure makes it simple to determine whether the user has secured the device with a password, PIN, or pattern. If he hasn’t, it could be a good idea to send a nice reminder to the user to secure the device using one of the options in the Android security settings.
Authenticating the device user
You can use the Android KeyguardManager.createConfirmDeviceCredentialIntent API to provide an additional layer of security that ensures that the user attempting to log in to your app is truly the device owner. If the user has configured the device with a security PIN, password, or pattern, this will bring up a system window for the user to verify the device credentials and provide you with a pass or fail.
Protecting Randomization Parameters
Randomization parameters are input parameters to cryptographic algorithms that introduce a random element to the functions to avoid the method producing the same outputs with the same input parameters, which would expose the implementation to brute-force attacks. Salt and Initialization Vector are two factors that introduce randomness.
Because these parameters are required for a hacker to extract a credential from a cryptographic function, they should be securely maintained as well. You have the option of encrypting them with a secure key stored in the key store or storing them in a safe preference container. For additional information, see my blog post on saving Secure Preferences.
Android’s KeyStore System
The KeyChain API and the Android Keystore provider both leverage the Android Keystore system for secure credential storage. Android 4.3 introduced the Keystore provider (API level 18).
If you wish to store credentials that are accessible to other apps, you can utilize the KeyChain API. You can select the credentials by the user through a system-provided UI, similar to the permission ‘Allow/Deny’ dialogs.
For the purposes of this tutorial, we will assume that the credentials should be available exclusively to the app.
The Android Keystore Provider
The Android Keystore Provider interface allows you to store credentials that only the app has access to. It offers the same security benefits as the KeyChain API but without the need for user participation. This is the option we will be using in this tutorial.
Creating a salted hash
As previously said, we want to avoid a hacker deriving the actual password from anything we’re saving, hence the simplest approach to do this is to generate a salted one-way hash.
You can build a SHA-256 or SHA-512 hash and then convert it to Base64, save it using Java.security MessageDigest class.
To add the salt, use the message digest object’s update() method.
Creating the secret key for encryption
You can use the Android Key Generator using an algorithm to generate a secret key for encryption. For AES encryption, use KEY ALGORITHM AES.
Then, using an alias for your key, you can use the KeyGenParameterSpec.Builder to provide the key size (e.g. 256), block mode (e.g. CBC), and padding (PKCS7) of the AES encryption.
You can initialize the KeyGenerator using the KeyGenParameterSpec and use it to produce the key.
Retrieving the secret key from the Key Store
To get your private key from the Key Store, you must first obtain an instance of the KeyStore by specifying the AndroidKeyStore provider.
Then, using your key alias from the previous step, call the getEntry() method to retrieve a SecretKey object containing your key information.
Encrypting the password Hash
To encrypt the hash, you must first obtain an instance of the Cipher class by passing your cipher algorithm.
You can initialize the encryption by sending in the mode (ENCRYPT MODE), your secret key, and an ivParameterSpec along with your Initialization Vector.
The encrypted result is a byte array returned by cipher.doFinal(). The encrypted password can then be stored by transforming the byte array to a Base64 string.
Decrypting the password Hash
Decrypting the password hash using the same cipher method and IV as you used for encryption.
As previously mentioned, you obtain a private key from the key store. Then, using your cipher technique, you obtain a Cipher instance. The cipher is initialized with DECRYPT MODE, your secret key, and the IvParameterSpec.
Before utilizing the cipher method to decrypt, the password hash must be Base64 that decode into a byte array.
The result of cipher.doFinal() will be a byte array, which you can then convert to a String using UTF8 encoding.
This post aims to share some ideas for safely saving passwords on Android. The safest method is, of course, to not keep passwords at all. We spoke about potential security issues with passwords and how to deal with them.