More Secure than Yesterday - Storing the Users Passwords

Today we’re tackling the common problem of keeping our users passwords secure. It’s going to be a recap of the best practices and reasons behind them, and I’ll be introducing a very useful library for cryptography in Java to have a concrete implementation of how you can store your user’s passwords in a safe way.

About algorithms and practices

You should not use plaintext passwords - this is the wrong way to do it and you have no excuse for still using it. It’s bad for the obvious reasons:

  • Data theft - sure you make think you have in place that would prevent an outsider to but on the other hand a lot of people use the same password for their email, twitter, facebook account, etc. The user entrusts you with his data, so are you REALLY, REALLY 100% confident there is absolutely NO WAY an attacker could get access to the password field in your database? No sql injection in your code, no backdoor to your server, no bug in the database.

  • Trusting people - in most of the cases you are not the sole person with access to the database. Even if you are the administrator, save yourself from the temptation of trying your users passwords on their provided emails.

What to do instead?

Well there are two way to look at the cryptography methods. There is Two Way (Reversible) encryption algorithms which means once you encrypt a text you can also decrypt it later, and One Way (NOT reversible) also known as Hashing functions or Digest algorithms. Among the most used hashing algorithms were(MD5) and now SHA1(Git uses SHA1):

  • MD5 - no longer considered reliable as a HASH function (this is not neceseraly what’s caused it’s demise as the default algorithm to use for password hashing, although the missunderstood fear must have contributed a lot). A colission happens when two diferent string like ‘123456’ and ‘str0NG!Pass’ have the same hash. So theoretically the attacker might need to try a reduced set of dictionary words. But hashing functions inevitable have collisions since they are limited in output. I mean you can MD5 hash a whole document and it’s output will still need be 32 chars. So when you are pressing the representation of a bigger string into a smaller one, it’s innevitable to have collisions, the questions is how often they occur. See Collision Resistance for more details. But the real reason why MD5 fails as a password hashing mechanism it’s because it’s fast, as we’ll see below.
  • SHA familly - SHA1 and SHA-2 variants: SHA-224, SHA-256, SHA-384, SHA-512

see here for a comparison.

You’d say there’s not much use for Hashing function if you cannot get back the password you “encrypted” and stored because you cannot validate the user when he presents his password. Actually it turns out a Hashing function is what you want to use, because you don’t have to know the password of the user if you’d proceed like this:

If you were to store it encrypted with a reversible algorithm, the attacker(who let’s say has in some way access to your users password data) would only need to know the key(which could be in a properties file on the server), the algorithm you used for encryption and he will then have access to all passwords.

There is one case in which you’d need to choose a TWO way encryption though. If you are also using some legacy service that authenticates against the plain-text password like for example a FTP account. Apart from this there is little reason why you should not always use password hashing.

What if a user loses his password how can you remind it to him?

Simple answer is that you cannot and you should instead offer the user the possibility to reset his password to a new one which you send by email and advise the user to change it as soon as possible since the email is not a secure medium for holding a password. A site that send you back your original password today is laughed in IT business because it means it shows little respect for user privacy and is a service you should move away from. If you are the owner of such a site it means bad publicity for you but luckily read below how you can fix this.

Luckily there is an easy way to protect against this by:

    <!-- only useful for Spring usage -->
    <dependency>
        <groupId>org.jasypt</groupId>
        <artifactId>jasypt-spring3</artifactId>
        <version>1.9.0</version>
    </dependency>

The base class for implementing digest is <b>StandardStringDigester</b> which does all the things:
<ol>
<li><b>Use a salt and attach it to the password</b></li>
<li><b>Encrypt by applying hash</b></li>
<li><b>Iterate the hash function</b></li>
<li><b>Encode the result with BASE64</b> - because we it's safer to get the string encoded to Base64 so that it's easier to store in a DB and you're not dependent of the DB character encoding</li>
<li><b>Provide simple method to validate the password on login</b></li>
</ol>

```java
StandardStringDigester digester = new StandardStringDigester();
digester.setAlgorithm("SHA-256"); //we set the algorithm
digester.setIterations("2000");//number of iterations  
digester.setSaltSizeBytes(32); 
digester.setSaltGenerator(SaltGenerator); //if we do not set it explicit, the default is RandomSaltGenerator
...
String digest = digester.digest(userPassword); //we store the digest to persistence storage
...
//

//For authentication we just need to do:
if (digester.matches(inputPassword, storedDigest)) {
  // user authenticated
} else {
  // bad login!
}

But wait, where is the salt part retrieve and storage done? It’s already contained in the digest string. Since we setSaltSizeBytes(nr_bytes) we know what part of the digest is the salt information. So while it’s not stored in a different field, it can still be extracted.

Actually in a multithreaded environment you are better off using

PooledStringDigester digester = new PooledStringDigester();
digester.setPoolSize(4);// This would be a good value for a 4-core system 
digester.setAlgorithm("SHA-1");
.... //all the methods used as above

Spring 3.1 usage

Jasypt also makes Spring integration very easy by providing it’s own namespace

<beans xmlns="http://www.springframework.org/schema/beans"
       ...
       xmlns:encryption="http://www.jasypt.org/schema/encryption"
       ...
       xsi:schemaLocation="http://www.springframework.org/schema/beans
                           http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
                           ...
                           http://www.jasypt.org/schema/encryption
                           http://www.jasypt.org/schema/encryption/jasypt-spring3-encryption-1.xsd
                           ...">

<!-- Construct a PooledStringDigester bean that can be referenced. All digesters and encryptors in jasypt are thread-safe -->
<encryption:string-digester id="password-digester" pool-size="4" algorithm="SHA-256" salt-size-bytes="16" iterations="50000"/>

</beans>

Conclusions

  • No excuse to use cleartext passwords anymore
  • If you provide the user’s lost password in an email to him it makes you services look lame. Some of your users will understand that your site has a major security issue.
  • Don’t try to implement your own security algorithm, there are mathematical geniuses way smarter than me and you who’s passion is to think about stuff like this.
  • Use a hashing algorithm, preferably from the SHA-2 family or better PBKDF2(which by design run slower).
  • Use a random salt
  • Iterate hash
  • Use library which makes security easy

Hope you got some benefit for the article. Till next time… have fun.