SEC-2122: Change doc to prioritize bcrypt use
This commit is contained in:
parent
34893cd53a
commit
d6524feb62
|
@ -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 {
|
||||
|
|
|
@ -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.
|
||||
* <p>
|
||||
* The digest algorithm is invoked on the concatenated bytes of the salt, secret and password.
|
||||
* <p>
|
||||
* 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
|
||||
|
|
|
@ -219,14 +219,27 @@
|
|||
<title>Password Encoding</title>
|
||||
<para xlink:href="#spring-security-crypto-passwordencoders">Spring Security's
|
||||
<interfacename>PasswordEncoder</interfacename> 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 <quote>hashed</quote> using a digest algorithm such as MD5 or
|
||||
SHA. Spring Security 3.1's <link xlink:href="#spring-security-crypto-passwordencoders"
|
||||
><literal>crypto</literal></link> 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
|
||||
<literal>org.springframework.security.authentication.encoding</literal> as legacy
|
||||
implementations. The <classname>DaoAuthenticationProvider</classname> 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
|
||||
<code>org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder"</code>
|
||||
is a good choice for security. There are also compatible implementations in other common
|
||||
programming languages so it a good choice for interoperability too.</para>
|
||||
<para>
|
||||
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,
|
||||
<literal>org.springframework.security.authentication.encoding</literal>.
|
||||
The <classname>DaoAuthenticationProvider</classname> can be injected
|
||||
with either the new or legacy <interfacename>PasswordEncoder</interfacename>
|
||||
types.</para>
|
||||
<section>
|
||||
|
@ -251,7 +264,8 @@
|
|||
<title>Adding Salt to a Hash</title>
|
||||
<para> 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
|
||||
<literal>5f4dcc3b5aa765d61d8327deb882cf99</literal> using google, you will quickly
|
||||
find the original word <quote>password</quote>. 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).</para>
|
||||
<para>The <classname>StandardPasswordEncoder</classname> in the <link
|
||||
xlink:href="#spring-security-crypto-passwordencoders"
|
||||
><literal>crypto</literal></link> package uses a random 8-byte salt, which is stored
|
||||
in the same field as the password.<note>
|
||||
<para>The legacy approach to handling salt was to inject a
|
||||
<para>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.
|
||||
<note><para>The legacy approach to handling salt was to inject a
|
||||
<interfacename>SaltSource</interfacename> into the
|
||||
<classname>DaoAuthenticationProvider</classname>, which would obtain a salt
|
||||
value for a particular user and pass it to the
|
||||
<interfacename>PasswordEncoder</interfacename>. 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.</para>
|
||||
<interfacename>PasswordEncoder</interfacename>. 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.</para>
|
||||
</note></para>
|
||||
</section>
|
||||
<section>
|
||||
|
@ -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.</para>
|
||||
going further and attempting to authenticate through your application. Using a standard
|
||||
like bcrypt will avoid these issues.
|
||||
</para>
|
||||
<para>If you want to generate encoded passwords directly in Java for storage in your
|
||||
user database, then you can use the <methodname>encode</methodname> method on the
|
||||
<interfacename>PasswordEncoder</interfacename>.</para>
|
||||
|
|
|
@ -120,18 +120,19 @@ public interface PasswordEncoder {
|
|||
This method is designed to support password-based authentication schemes.
|
||||
</para>
|
||||
<para>
|
||||
The <classname>StandardPasswordEncoder</classname> 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 <classname>BCryptPasswordEncoder</classname> 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.
|
||||
</para>
|
||||
<programlisting language="java"><![CDATA[
|
||||
StandardPasswordEncoder encoder = new StandardPasswordEncoder("secret");
|
||||
// Create an encoder with strength 16
|
||||
BCryptPasswordEncoder encoder = new BCryptPasswordEncoder(16);
|
||||
String result = encoder.encode("myPassword");
|
||||
assertTrue(encoder.matches("myPassword", result));]]>
|
||||
</programlisting>
|
||||
<para>
|
||||
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.
|
||||
</para>
|
||||
</section>
|
||||
|
||||
</chapter>
|
||||
|
|
|
@ -400,13 +400,16 @@
|
|||
namespace. </para>
|
||||
<section xml:id="ns-password-encoder">
|
||||
<title>Adding a Password Encoder</title>
|
||||
<para> Often your password data will be encoded using a hashing algorithm. This is
|
||||
supported by the <literal><password-encoder></literal> element. With SHA
|
||||
<para> 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
|
||||
<literal><password-encoder></literal> element. With bcrypt
|
||||
encoded passwords, the original authentication provider configuration would look
|
||||
like this: <programlisting language="xml"><![CDATA[
|
||||
<beans:bean name="bcryptEncoder" class="org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder"/>
|
||||
|
||||
<authentication-manager>
|
||||
<authentication-provider>
|
||||
<password-encoder hash="sha"/>
|
||||
<password-encoder ref="bcryptEncoder"/>
|
||||
<user-service>
|
||||
<user name="jimi" password="d7e6351eaa13189a5a3641bab846c8e8c69ba39f"
|
||||
authorities="ROLE_USER, ROLE_ADMIN" />
|
||||
|
@ -417,20 +420,10 @@
|
|||
</authentication-manager>
|
||||
]]>
|
||||
</programlisting></para>
|
||||
<para> 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 <classname>UserDetails</classname> object which
|
||||
is loaded by your <classname>UserDetailsService</classname>. For example, to use
|
||||
the <literal>username</literal> property, you would use <programlisting language="xml"><![CDATA[
|
||||
<password-encoder hash="sha">
|
||||
<salt-source user-property="username"/>
|
||||
</password-encoder>
|
||||
]]></programlisting> You can use a custom password encoder bean by using the
|
||||
<literal>ref</literal> attribute of <literal>password-encoder</literal>. This
|
||||
should contain the name of a bean in the application context which is an
|
||||
instance of Spring Security's <interfacename>PasswordEncoder</interfacename>
|
||||
interface. </para>
|
||||
<para>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.
|
||||
</para>
|
||||
</section>
|
||||
</section>
|
||||
</section>
|
||||
|
|
Loading…
Reference in New Issue