Say no to plain passwords: Secure Password Hashing
Plaintext passwords should not be used anymore. You are probably using a web shop software framework that takes already care of this. Most application frameworks in the world do not use plain passwords anymore but password hashes instead.
This article was also published on LinkedIn.
Then, as a provider or developer you do not need to take care of this issue and this is great!
However, from time to time I stumble upon a web service that sends me a copy of my registration information as unencrypted e-mail – including my password. This is not only unnecessary and insecure but makes me wonder how passwords are stored in these cases. This mostly happens when web applications are completely tailor-made and no one takes proper care of password hashing and the process around it (like resetting a password in case a user forgets it).
Be advised: You should not implement password hashing “by hand” if you have no idea what you are doing! Use a proper framework!
Why hashing Passwords?
Simple question, easy answer: Passwords are still the most common authentication attribute for all kinds of applications and the average user tends to reuse “good” passwords. Hence, to protect the confidentiality of your user's passwords, passwords should not be stored in plain but only cryptographically secured. Password hashes are the most simple way to do this. As I said, most software frameworks dealing with passwords take already care of this. Other examples are account passwords used in operating systems like Linux, Windows or mobile systems. Secure hashing has become state-of-the-art.
I heard password hashes are being cracked, how is that possible?
Over the years, crypto experts and system developers have learned from hackers and how they attempt to crack hashes by optimized brute-force-attacks. A brute-force-attack is an attempt to try out all possible input combinations of a certain mathematical function. The attacker compares the result with an expected result. This could, for instance, be an intercepted password hash. If both match the attacker has found the original plain text. How could a password hash be “intercepted”? Hackers attack web applications through all kinds of security holes, so simply imagine an attacker had gained access to a web shop's database backend.
This is why a secure hash function is not enough to create a secure hash in all cases. If you would hash credit card numbers (or IP addresses) without further measures, the input space for the hash function would be very small. Hashing all possible credit card numbers and storing their hashes into a database would be possible. A brute-force-attack to restore the input values from the hash values would be successful. Additionally, there are optimized brute-force-attacks like “rainbow tables” which reduce the necessary computational effort dramatically.
Passwords are also relatively “small”. Moreover, since most passwords chosen by users are based on normal words, dictionary-attacks can be used for optimized brute-force-attacks. A dictionary-attack by the way does not mean that only words from dictionaries or other word collections are directly used but also variations and permutations based on these words. For instance, let's say “cat” is the next word in the list. A clever brute-force-attack would now ran all kinds of permutations against the hash function (this could include 1000s of variations), like “cat1”, “cat2”, “cat1!”, “cat2015”, “c4t” etc.
Secure Passwords
Please note that “cat1” is just used as an example but will never be a secure password, no matter which way of hashing is used. Besides being too short, it will show up on every brute-force-attack and hence it will be only a matter of time until it is discovered.
This is why the term “passphrase” has come to light, users are asked to use long arbitrary passwords based on personal unpredictable sentences which include some random numbers and special characters. Strong passwords are and always will be the basis for a secure password-based authentication.
Security Improvement of calculating hashes
In the context of password hashing, have you ever heard of “salt” and “iteration count”? These terms provide the state-of-the-art of password hashing today. The iteration count is simply said the number of times which the hash function is used on a single password. Your password is not only hashed once, the hash value itself is hashed again and again. If we use an iteration count of 100000, every attacker has to hash all passwords that are being attacked 100000 times which enlarges the computational effort. However, rainbow tables, for instance, reduce the computational effort in these cases as well.
An additional, individual salt per password makes attacks like rainbow tables infeasible. A salt is a long random value that is added to the password each time its hash has to be calculated. In practice, that means an application has to store additional values to create and verify passwords: the iteration count (globally or per password), the salt (per password) and the password hash value. That applies, if a single hash function is used. Otherwise, the hash function has to be stored per password as well.
The iteration count and the length of the salt utilize a secure hash function actually secure!
That being said, how to implement all these attributes for a secure hashing algorithm? The answer: Use a standard implementation! There are several ways to implement all the discussed attributes. For instance, you could simply concatenate the salt with the password. However, implementations that are often being used, cryptographically prefer an HMAC function to do this by using the password as key and the salt as input. There are several other cryptographic aspects that improve security – especially when using an HMAC. So, it is a good advice to use a standard implementation and not to implement secure hashing by hand! One common standard that is used by a large number of applications is PBKDF2 from PKCS#5, for instance by TrueCrypt and its secure successors.
PBKDF2 is also future-ready. At least as long as common hash functions are considered to be secure. The iteration count has no theoretical maximum and hence can be used to adjust the computational effort for attackers on newer hardware. Of course, adjusting the iteration count in your application requires re-hashing all user passwords. This could be accomplished, for instance, by forcing users to change their passwords when they log in the next time.
Processes around Password hashing
If you have a web shop or a web portal of any kind that uses user accounts with password authentication, you have to consider the fact that you do not have access to the plain passwords of your user base. This is not a problem at all if you use modern web standards. If you want to switch from plain passwords, that might cause some trouble. Take password resetting as an example. If you use plain passwords, a password resetting function could be realized by mailing users their existing password. Since mails are usually sent unencrypted, mailing a password that has been securely set using HTTPS is a bad idea anyway. Using password hashes, this would no longer be possible. Common practice, however, is to generate a one-time-link that allows for setting a new password. That link is sent to the user's mail address. The password reset function simply creates a new hash based on the new password. Please note that it is good practice to use a new salt every time a new password is hashed.
If your application uses encryption based on user passwords, you can use Key-Wrapping to mitigate problems with password changes.
When a password change has to be forced for various reasons, you want to prevent users to provide their old password. By simply hashing the new password with the parameters of the old one, you can verify if the old password equals the new one.
Secure Password hashing Example
Here is an implementation example of PBKDF2 for Java-based web applications using CrococryptLib:
//Creating a PBKDF2 based secure password hash with default settings:
//100000 iterations, HMAC_SHA512, 512 bit salt, 512 bit hash
CryptoMain.setDefaultHashIterationCount(100000);
CompactPasswordHash cph = CompactPasswordHash.generateHash(new Secret("DemoPassword"));
//Store the compact hash format in various output formats.
//This includes the complete hash including
//all necessary parameters (algorithm, salt, iteration count).
System.out.println("Hash (Hex): " + cph.writeToHex());
System.out.println("Hash (B64): " + cph.writeToBase64());
byte[] raw = cph.writeToBytes();
//Let's assume 'raw' had been written to a database and was being reloaded.
CompactPasswordHash cph_loaded = CompactPasswordHash.newInstance();
cph_loaded.readFromBytes(raw);
//Perform a validation check of the newly provided password against the stored hash
System.out.println(cph_loaded.verifyPassword(new Secret("DemoPassword")));
//Perform a validation check that has to fail
System.out.println(cph_loaded.verifyPassword(new Secret("wrongPassword")));
You can download this running example within the CrococryptLib SDK and use it in your own application.
About
Computer Scientist Frank Hissen is the owner of HissenIT, a small business in Germany focusing on IT security software development and consulting. Frank has over 19 years experience in various positions as security expert in IT projects. He mainly worked for large businesses but also medium-sized companies.
Keywords
Passwords, Password Security, Password Hashing, Password Encryption, Secure Hashing Algorithms, Password Cracking, Password Hacking
Categories: IT Security Background articles Development/Java
Comments
Post your comment
Share
If you like this page, it would be a great thing if you share it with others: