2
0
Эх сурвалжийг харах

SEC-2122: Change doc to prioritize bcrypt use

Luke Taylor 12 жил өмнө
parent
commit
d6524feb62

+ 3 - 0
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 {

+ 3 - 0
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.
  * <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

+ 33 - 20
docs/manual/src/docbook/core-services.xml

@@ -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>

+ 8 - 7
docs/manual/src/docbook/crypto.xml

@@ -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>

+ 10 - 17
docs/manual/src/docbook/namespace-config.xml

@@ -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>&lt;password-encoder&gt;</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>&lt;password-encoder&gt;</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>