I’ve received an intriguing request from a customer last week : he wanted to know if we’ve done benchmarks of the password hashing schemes that are available in OpenDJ, our LDAP directory service. Their fear was that with stronger schemes, they could not sustain a high authentication rate.
In light of the LinkedIn leak of several millions of passwords, hashed with a simple unsalted SHA1, I decided to run a quick and simple test.
SSHA1 is the default hashing scheme for password in OpenDJ. The salt is an 8 bytes (64-bit) random string and is used with the password to produce the 20 bytes message digest. But OpenDJ directory server supports a wide range of password hashing scheme and salted SHA512 is currently the most secure hashing algorithm we support (and the salt here is also an 8 bytes (64-bit) random octet string).
So for the test, I generated a sample directory data set with 10 000 users, and imported it in the OpenDJ directory (a 2.5 development build) with the default settings, on my laptop (MacBook Pro, 2.2 GHz intel Core i7).
$ ldapsearch -D "cn=directory manager" -w secret12 -p 1389 -b "dc=example,dc=com" 'uid=user.10' dn userPassword dn: uid=user.10,ou=People,dc=example,dc=com userPassword: {SSHA}cchzM+LrPCvbZdthOC8e62d4h7a4CfoNvl6d/w==
I then ran an “authrate” which is a small benchmark tool that allows to stress an LDAP server with a high number of authentications (LDAP Bind requests) and let it run to 5 minutes.
authrate -h localhost -p 1389 -g 'rand(0,10000)' -D "uid=user.%d,ou=people,dc=example,dc=com" -w password -c 32 -f ----------------------------------------------------------------- Throughput Response Time (ops/second) (milliseconds) recent average recent average 99.9% 99.99% 99.999% err/sec ----------------------------------------------------------------- ... 26558.0 26148.9 1.179 1.195 10.168 19.431 156.421 0.0
I then stopped the server, changed the import default password encryption scheme to Salted SHA512, and reimported the data.
$ ldapsearch -D "cn=directory manager" -w secret12 -p 1389 -b "dc=example,dc=com" 'uid=user.10' dn userPassword dn: uid=user.10,ou=People,dc=example,dc=com userPassword: {SSHA512}eTGiwtTM4niUKNkEBy/9t03UdbsyYTL1ZXhy6uFnw4X0T6Y9Zf5/dS7hDIdx3/UTlUQ/9JjNV9fOg2BkmVgBhWWu5WpWKPog
And then re-run the “authrate”
$ authrate -h localhost -p 1389 -g 'rand(0,10000)' -D "uid=user.,ou=people,dc=example,dc=com" -w password -c 32 -f ----------------------------------------------------------------- Throughput Response Time (ops/second) (milliseconds) recent average recent average 99.9% 99.99% 99.999% err/sec ----------------------------------------------------------------- ... 25481.7 25377.6 1.222 1.227 10.470 15.473 158.234 0.0
As you can see, there is not much of a difference in throughput or response time, when using the strongest algorithm to hash user password. So do not hesitate to change the default settings and make use of the strongest password hashing schemes with OpenDJ. It could save you from the embarrassment of, one day, contacting each of your users or customers to ask them to change their compromised password.
The default password hashing schemes are in 2 locations :
- The default password policy for all passwords that are changed online.
dn: cn=Default Password Policy,cn=Password Policies,cn=config ds-cfg-default-password-storage-scheme: cn=Salted SHA-512,cn=Password Storage Schemes,cn=config
- In the Import Password Policy
dn: cn=Password Policy Import,cn=Plugins,cn=config ds-cfg-default-user-password-storage-scheme: cn=Salted SHA-512,cn=Password Storage Schemes,cn=config
Both properties can be changed with dsconfig while the OpenDJ server is running, and the new scheme will be used for all subsequent operations.
Is that one salt per password or one global salt?
If per password, where do you keep the salt?
It is a salt per password and the salt is appended to the hashed password in clear.
This is the OWASP recommended way to hash passwords : https://www.owasp.org/index.php/Hashing_Java.
The additional step for very long term passwords storage, would be to iterate on the hash multiple times (I think the recommandation is a minimum of 1000 iterations, which will probably start to become a performance bottleneck for us).
What surprises me is that the salt is stored in the same table as the password. It means that for a specific user, I can build my salted brute force dictionary and find it if the password is common enough.
Salt is really here to save from massive steal.
I personally would have stored the salt for each user on a different box or at least data store than the hashed password.
Yes, the salt is here to prevent attacks with precomputed hashes (http://en.wikipedia.org/wiki/Rainbow_table), which may correspond to several passwords as well.
The use of Salted SHA 512, over SHA1 reduces the risk of chain collisions.
Now, storing the salt separated from the password would definitely be more secure, but it increases the complexity when distributing the passwords and also consumes much more memory, space or disk IOs. Each time you need to authenticate someone, you will need to read in 2 locations the hashed password and the salt. You also need to associate the hash and the salt using an extra ID which increases the risk of errors.
Overall, the use of stronger and salted hash mechanism is meant to make it harder and longer to run dictionary attacks but it is just one of the steps to secure the use of plain text passwords. The other existing measures are complexity check of the passwords, as well as forcing users to change regularly the password (although this often results in simpler passwords to remember), and not to reuse previous passwords. All of these are available and configurable in OpenDJ, through the Password Policies.
Can other password encryption algorithms be added to OpenDJ, such as bcrypt/scrypt?
Yes, of course. There is an interface that all password storage schemes are implementing. The encryption algo can be reversible or not, but OpenDJ directory server will not return a cleartext password to the client application. Decrypt (when available) is only used for internal use, for example with authentication mechanisms that passphrases and multiple steps (like Digest-MD5).
Does OpenDJ support SCRYPT as a Password scheme out of the box? If not when will the support be available?
Scrypt is not a supported password scheme yet. It’s possible to implement it as a plugin. But there is no plan to do so in the near term. These cost based algorithms are preventing the server from being able to scale authentications.