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 EncodingSpring 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.
+