From d6524feb62fd0c1cc6376827b9218cc342f5e289 Mon Sep 17 00:00:00 2001 From: Luke Taylor Date: Fri, 17 May 2013 18:42:47 +0100 Subject: [PATCH] SEC-2122: Change doc to prioritize bcrypt use --- .../crypto/password/PasswordEncoder.java | 3 ++ .../password/StandardPasswordEncoder.java | 3 ++ docs/manual/src/docbook/core-services.xml | 53 ++++++++++++------- docs/manual/src/docbook/crypto.xml | 15 +++--- docs/manual/src/docbook/namespace-config.xml | 27 ++++------ 5 files changed, 57 insertions(+), 44 deletions(-) diff --git a/crypto/src/main/java/org/springframework/security/crypto/password/PasswordEncoder.java b/crypto/src/main/java/org/springframework/security/crypto/password/PasswordEncoder.java index c4320f31a6..08591883d4 100644 --- a/crypto/src/main/java/org/springframework/security/crypto/password/PasswordEncoder.java +++ b/crypto/src/main/java/org/springframework/security/crypto/password/PasswordEncoder.java @@ -17,6 +17,9 @@ package org.springframework.security.crypto.password; /** * Service interface for encoding passwords. + * + * The preferred implementation is {@code BCryptPasswordEncoder}. + * * @author Keith Donald */ public interface PasswordEncoder { diff --git a/crypto/src/main/java/org/springframework/security/crypto/password/StandardPasswordEncoder.java b/crypto/src/main/java/org/springframework/security/crypto/password/StandardPasswordEncoder.java index 96651d3749..f850714335 100644 --- a/crypto/src/main/java/org/springframework/security/crypto/password/StandardPasswordEncoder.java +++ b/crypto/src/main/java/org/springframework/security/crypto/password/StandardPasswordEncoder.java @@ -28,6 +28,9 @@ import org.springframework.security.crypto.keygen.KeyGenerators; * random 8-byte random salt value. It uses an additional system-wide secret value to provide additional protection. *

* The digest algorithm is invoked on the concatenated bytes of the salt, secret and password. + *

+ * If you are developing a new system, {@link org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder} is + * a better choice both in terms of security and interoperability with other languages. * * @author Keith Donald * @author Luke Taylor diff --git a/docs/manual/src/docbook/core-services.xml b/docs/manual/src/docbook/core-services.xml index 74097c4b92..3df8556cef 100644 --- a/docs/manual/src/docbook/core-services.xml +++ b/docs/manual/src/docbook/core-services.xml @@ -219,14 +219,27 @@ Password Encoding Spring Security's PasswordEncoder interface is used to support the use of - passwords which are encoded in some way in persistent storage. This will normally mean - that the passwords are hashed using a digest algorithm such as MD5 or - SHA. Spring Security 3.1's crypto package introduces a simpler API which encourages - best-practice for password hashing. We would encourage you to use these APIs for new - development and regard the classes in package - org.springframework.security.authentication.encoding as legacy - implementations. The DaoAuthenticationProvider can be injected + passwords which are encoded in some way in persistent storage. You should never store + passwords in plain text. Always use a one-way password hashing algorithm such as bcrypt + which uses a built-in salt value which is different for each stored password. Do not use + a plain hash function such as MD5 or SHA, or even a salted version. Bcrypt is deliberately + designed to be slow and to hinder offline password cracking, whereas standard hash algorithms + are fast and can easily be used to test thousands of passwords in parallel on custom + hardware. You might think this doesn't apply to you since your password database is + secure and offline attacks aren't a risk. If so, do some research and read up on all + the high-profile sites which have been compromised in this way and have been pilloried + for storing their passwords insecurely. It's best to be on the safe side. Using + org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder" + is a good choice for security. There are also compatible implementations in other common + programming languages so it a good choice for interoperability too. + + If you are using a legacy system which already has hashed passwords, then you will + need to use an encoder which matches your current algorithm, at least until you can + migrate your users to a more secure scheme (usually this will involve asking the user + to set a new password, since hashes are irreversible). Spring Security has a package + containing legacy password encoding implementation, namely, + org.springframework.security.authentication.encoding. + The DaoAuthenticationProvider can be injected with either the new or legacy PasswordEncoder types.

@@ -251,7 +264,8 @@ Adding Salt to a Hash One potential problem with the use of password hashes that it is relatively easy to get round the one-way property of the hash if a common word is used for the - input. For example, if you search for the hash value + input. People tend to choose similar passwords and huge dictionaries of these from + previously hacked sites are available online. For example, if you search for the hash value 5f4dcc3b5aa765d61d8327deb882cf99 using google, you will quickly find the original word password. In a similar way, an attacker can build a dictionary of hashes from a standard word list and use this to lookup the @@ -263,19 +277,16 @@ value is usually preferable to none. Using a salt means that an attacker has to build a separate dictionary of hashes for each salt value, making the attack more complicated (but not impossible). - The StandardPasswordEncoder in the crypto package uses a random 8-byte salt, which is stored - in the same field as the password. - The legacy approach to handling salt was to inject a + Bcrypt automatically generates a random salt value for each password when it + is encoded, and stores it in the bcrypt string in a standard format. + The legacy approach to handling salt was to inject a SaltSource into the DaoAuthenticationProvider, which would obtain a salt value for a particular user and pass it to the - PasswordEncoder. Using a random salt and - combining it with the password data field means you don't have to worry about - the details of salt handling (such as where the the value is stored), as it is - all done internally. So we'd strongly recommend you use this approach unless you - already have a system in place which stores the salt separately. + PasswordEncoder. Using bcrypt means you don't have + worry about the details of salt handling (such as where the the value is stored), + as it is all done internally. So we'd strongly recommend you use bcrypt + unless you already have a system in place which stores the salt separately.
@@ -296,7 +307,9 @@ be using upper-case while the output from the encoder is lower-case. Make sure you write a test to check the output from your configured password encoder with a known password and salt combination and check that it matches the database value before - going further and attempting to authenticate through your application. + going further and attempting to authenticate through your application. Using a standard + like bcrypt will avoid these issues. + If you want to generate encoded passwords directly in Java for storage in your user database, then you can use the encode method on the PasswordEncoder. diff --git a/docs/manual/src/docbook/crypto.xml b/docs/manual/src/docbook/crypto.xml index e811c6aa7d..000520d433 100644 --- a/docs/manual/src/docbook/crypto.xml +++ b/docs/manual/src/docbook/crypto.xml @@ -120,18 +120,19 @@ public interface PasswordEncoder { This method is designed to support password-based authentication schemes. - The StandardPasswordEncoder implementation applies 1024 iterations of the SHA-256 hashing algorithm to the rawPassword combined with a site-wide secret and 8-byte random salt: + The BCryptPasswordEncoder implementation uses the widely supported "bcrypt" algorithm + to hash the passwords. Bcrypt uses a random 16 byte salt value and is a deliberately slow algorithm, in order to + hinder password crackers. The amount of work it does can be tuned using the "strength" parameter which takes values + from 4 to 31. The higher the value, the more work has to be done to calculate the hash. The default value is 10. + You can change this value in your deployed system without affecting existing passwords, as the value is also stored + in the encoded hash. - - The random salt ensures each hash is unique when the same password is used multiple times. - The site-wide secret should be stored in a safe place separate from where passwords are stored, and is used to protect against a bruce force attack in the event the database of passwords is compromised. - 1024 iterations of the hashing algorithm strengthens the key and makes it more difficult to compromise using a brute force attack. -
diff --git a/docs/manual/src/docbook/namespace-config.xml b/docs/manual/src/docbook/namespace-config.xml index b1ca9f1584..f7cf0614cc 100644 --- a/docs/manual/src/docbook/namespace-config.xml +++ b/docs/manual/src/docbook/namespace-config.xml @@ -400,13 +400,16 @@ namespace.
Adding a Password Encoder - Often your password data will be encoded using a hashing algorithm. This is - supported by the <password-encoder> element. With SHA + Passwords should always be encoded using a secure hashing algorithm designed for the purpose + (not a standard algorithm like SHA or MD5). This is supported by the + <password-encoder> element. With bcrypt encoded passwords, the original authentication provider configuration would look like this: + - + @@ -417,20 +420,10 @@ ]]> - When using hashed passwords, it's also a good idea to use a salt value to - protect against dictionary attacks and Spring Security supports this too. - Ideally you would want to use a randomly generated salt value for each user, but - you can use any property of the UserDetails object which - is loaded by your UserDetailsService. For example, to use - the username property, you would use - - - ]]> You can use a custom password encoder bean by using the - ref attribute of password-encoder. This - should contain the name of a bean in the application context which is an - instance of Spring Security's PasswordEncoder - interface. + Bcrypt is a good choice for most cases, unless you have a legacy system which forces + you to use a different algorithm. If you are using a simple hashing algorithm or, even worse, + storing plain text passwords, then you should consider migrating to a more secure option like bcrypt. +