SEC-1689: Minor doc updates related to use of password encoding and the crypto package.

This commit is contained in:
Luke Taylor 2011-03-17 01:44:10 +00:00
parent e470eaa41d
commit 1dc309b041
2 changed files with 68 additions and 57 deletions

View File

@ -73,24 +73,23 @@
authenticate is made.</para> authenticate is made.</para>
<section xml:id="core-services-erasing-credentials"> <section xml:id="core-services-erasing-credentials">
<title>Erasing Credentials on Successful Authentication</title> <title>Erasing Credentials on Successful Authentication</title>
<para> <para> By default (from Spring Security 3.1 onwards) the
By default (from Spring Security 3.1 onwards) the <classname>ProviderManager</classname> <classname>ProviderManager</classname> will attempt to clear any sensitive
will attempt to clear any sensitive credentials information from the credentials information from the <interfacename>Authentication</interfacename>
<interfacename>Authentication</interfacename> object which is returned by a successful object which is returned by a successful authentication request. This prevents
authentication request. This prevents information like passwords being retained longer information like passwords being retained longer than necessary. </para>
than necessary. <para> This may cause issues when you are using a cache of user objects, for example, to
</para> improve performance in a stateless application. If the
<para> <interfacename>Authentication</interfacename> contains a reference to an object in
This may cause issues when you are using a cache of user objects, for example, to the cache (such as a <interfacename>UserDetails</interfacename> instance) and this
improve performance in a stateless application. If the <interfacename>Authentication</interfacename> has its credentials removed, then it will no longer be possible to authenticate
contains a reference to an object in the cache (such as a <interfacename>UserDetails</interfacename> against the cached value. You need to take this into account if you are using a
instance) and this has its credentials removed, then it will no longer be possible to authenticate cache. An obvious solution is to make a copy of the object first, either in the
against the cached value. You need to take this into account if you are using a cache. An obvious cache implementation or in the <interfacename>AuthenticationProvider</interfacename>
solution is to make a copy of the object first, either in the cache implementation or in which creates the returned <interfacename>Authentication</interfacename> object.
the <interfacename>AuthenticationProvider</interfacename> which creates the returned Alternatively, you can disable the
<interfacename>Authentication</interfacename> object. Alternatively, you can disable the <literal>eraseCredentialsAfterAuthentication</literal> property on
<literal>eraseCredentialsAfterAuthentication</literal> property on <classname>ProviderManager</classname>. <classname>ProviderManager</classname>. See the Javadoc for more information.
See the Javadoc for more information.
</para> </para>
</section> </section>
<section xml:id="core-services-dao-provider"> <section xml:id="core-services-dao-provider">
@ -107,17 +106,13 @@
<bean id="daoAuthenticationProvider" <bean id="daoAuthenticationProvider"
class="org.springframework.security.authentication.dao.DaoAuthenticationProvider"> class="org.springframework.security.authentication.dao.DaoAuthenticationProvider">
<property name="userDetailsService" ref="inMemoryDaoImpl"/> <property name="userDetailsService" ref="inMemoryDaoImpl"/>
<property name="saltSource" ref="saltSource"/>
<property name="passwordEncoder" ref="passwordEncoder"/> <property name="passwordEncoder" ref="passwordEncoder"/>
</bean>]]></programlisting> The <interfacename>PasswordEncoder</interfacename> and </bean>]]></programlisting> The <interfacename>PasswordEncoder</interfacename> is optional. A
<interfacename>SaltSource</interfacename> are optional. A
<interfacename>PasswordEncoder</interfacename> provides encoding and decoding of <interfacename>PasswordEncoder</interfacename> provides encoding and decoding of
passwords presented in the <interfacename>UserDetails</interfacename> object that is passwords presented in the <interfacename>UserDetails</interfacename> object that is
returned from the configured <interfacename>UserDetailsService</interfacename>. A returned from the configured <interfacename>UserDetailsService</interfacename>. This
<interfacename>SaltSource</interfacename> enables the passwords to be populated with will be discussed in more detail <link xlink:href="#core-services-password-encoding"
a "salt", which enhances the security of the passwords in the authentication >below</link>. </para>
repository. These will be discussed in more detail <link
xlink:href="#core-services-password-encoding">below</link>. </para>
</section> </section>
</section> </section>
<section> <section>
@ -129,7 +124,8 @@
<para> <para>
<programlisting language="java"> <programlisting language="java">
UserDetails loadUserByUsername(String username) throws UsernameNotFoundException; UserDetails loadUserByUsername(String username) throws UsernameNotFoundException;
</programlisting> </para> </programlisting>
</para>
<para>The returned <interfacename>UserDetails</interfacename> is an interface that provides <para>The returned <interfacename>UserDetails</interfacename> is an interface that provides
getters that guarantee non-null provision of authentication information such as the getters that guarantee non-null provision of authentication information such as the
username, password, granted authorities and whether the user account is enabled or username, password, granted authorities and whether the user account is enabled or
@ -179,7 +175,8 @@
<interfacename>UserDetailsService</interfacename> to reuse the mapping files you've <interfacename>UserDetailsService</interfacename> to reuse the mapping files you've
probably already created. Returning to <literal>JdbcDaoImpl</literal>, an example probably already created. Returning to <literal>JdbcDaoImpl</literal>, an example
configuration is shown below:</para> configuration is shown below:</para>
<para> <programlisting language="xml"><![CDATA[ <para>
<programlisting language="xml"><![CDATA[
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource"> <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="org.hsqldb.jdbcDriver"/> <property name="driverClassName" value="org.hsqldb.jdbcDriver"/>
<property name="url" value="jdbc:hsqldb:hsql://localhost:9001"/> <property name="url" value="jdbc:hsqldb:hsql://localhost:9001"/>
@ -190,7 +187,8 @@
<bean id="userDetailsService" <bean id="userDetailsService"
class="org.springframework.security.core.userdetails.jdbc.JdbcDaoImpl"> class="org.springframework.security.core.userdetails.jdbc.JdbcDaoImpl">
<property name="dataSource" ref="dataSource"/> <property name="dataSource" ref="dataSource"/>
</bean> ]]> </programlisting> </para> </bean> ]]> </programlisting>
</para>
<para>You can use different relational database management systems by modifying the <para>You can use different relational database management systems by modifying the
<literal>DriverManagerDataSource</literal> shown above. You can also use a global <literal>DriverManagerDataSource</literal> shown above. You can also use a global
data source obtained from JNDI, as with any other Spring configuration.</para> data source obtained from JNDI, as with any other Spring configuration.</para>
@ -219,10 +217,18 @@
</section> </section>
<section xml:id="core-services-password-encoding"> <section xml:id="core-services-password-encoding">
<title>Password Encoding</title> <title>Password Encoding</title>
<para>Spring Security's <interfacename>PasswordEncoder</interfacename> interface is used to <para xlink:href="#spring-security-crypto-passwordencoders">Spring Security's
support the use of passwords which are encoded in some way in persistent storage. This <interfacename>PasswordEncoder</interfacename> interface is used to support the use of
will normally mean that the passwords are <quote>hashed</quote> using a digest algorithm passwords which are encoded in some way in persistent storage. This will normally mean
such as MD5 or SHA.</para> 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
with either the new or legacy <interfacename>PasswordEncoder</interfacename>
types.</para>
<section> <section>
<title>What is a hash?</title> <title>What is a hash?</title>
<para>Password hashing is not unique to Spring Security but is a common source of <para>Password hashing is not unique to Spring Security but is a common source of
@ -232,8 +238,8 @@
the string <quote>password</quote> (in hexadecimal) is the string <quote>password</quote> (in hexadecimal) is
<programlisting language="txt"> <programlisting language="txt">
5f4dcc3b5aa765d61d8327deb882cf99 5f4dcc3b5aa765d61d8327deb882cf99
</programlisting> A hash is </programlisting>
<quote>one-way</quote> in the sense that it is very difficult (effectively A hash is <quote>one-way</quote> in the sense that it is very difficult (effectively
impossible) to obtain the original input given the hash value, or indeed any impossible) to obtain the original input given the hash value, or indeed any
possible input which would produce that hash value. This property makes hash values possible input which would produce that hash value. This property makes hash values
very useful for authentication purposes. They can be stored in your user database as very useful for authentication purposes. They can be stored in your user database as
@ -254,16 +260,28 @@
<quote>salt</quote> when calculating the hashes. This is an additional string of <quote>salt</quote> when calculating the hashes. This is an additional string of
known data for each user which is combined with the password before calculating the known data for each user which is combined with the password before calculating the
hash. Ideally the data should be as random as possible, but in practice any salt hash. Ideally the data should be as random as possible, but in practice any salt
value is usually preferable to none. Spring Security has a value is usually preferable to none. Using a salt means that an attacker has to
<interfacename>SaltSource</interfacename> interface which can be used by an build a separate dictionary of hashes for each salt value, making the attack more
authentication provider to generate a salt value for a particular user. Using a salt complicated (but not impossible).</para>
means that an attacker has to build a separate dictionary of hashes for each salt <para>The <classname>StandardPasswordEncoder</classname> in the <link
value, making the attack more complicated (but not impossible).</para> 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
<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>
</note></para>
</section> </section>
<section> <section>
<title> Hashing and Authentication</title> <title> Hashing and Authentication</title>
<para>When an authentication provider (such as Spring Security's <para>When an authentication provider (such as Spring Security's
<classname>DaoAuthenticationProvider</classname> needs to check the password in a <classname>DaoAuthenticationProvider</classname>) needs to check the password in a
submitted authentication request against the known value for a user, and the stored submitted authentication request against the known value for a user, and the stored
password is encoded in some way, then the submitted value must be encoded using password is encoded in some way, then the submitted value must be encoded using
exactly the same algorithm. It's up to you to check that these are compatible as exactly the same algorithm. It's up to you to check that these are compatible as
@ -274,20 +292,13 @@
example, and your application is configured to use Spring Security's example, and your application is configured to use Spring Security's
<classname>Md5PasswordEncoder</classname>, there are still things that can go wrong. <classname>Md5PasswordEncoder</classname>, there are still things that can go wrong.
The database may have the passwords encoded in Base 64, for example while the The database may have the passwords encoded in Base 64, for example while the
enocoder is using hexadecimal strings (the default)<footnote> encoder is using hexadecimal strings (the default). Alternatively your database may
<para>You can configure the encoder to use Base 64 instead of hex by setting the be using upper-case while the output from the encoder is lower-case. Make sure you
<literal>encodeHashAsBase64</literal> property to <literal>true</literal>. Check write a test to check the output from your configured password encoder with a known
the Javadoc for <classname>MessageDigestPasswordEncoder</classname> and its password and salt combination and check that it matches the database value before
parent classes for more information.</para> going further and attempting to authenticate through your application.</para>
</footnote>. Alternatively your database may be using upper-case while the output <para>If you want to generate encoded passwords directly in Java for storage in your
from the encoder is lower-case. Make sure you write a test to check the output from user database, then you can use the <methodname>encode</methodname> method on the
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. For more information on the default method
for merging salt and password, see the Javadoc for
<classname>BasePasswordEncoder</classname>. If you want to generate encoded
passwords directly in Java for storage in your user database, then you can use the
<methodname>encodePassword</methodname> method on the
<interfacename>PasswordEncoder</interfacename>.</para> <interfacename>PasswordEncoder</interfacename>.</para>
</section> </section>
</section> </section>

View File

@ -109,7 +109,7 @@ KeyGenerators.string();]]>
<title>Password Encoding</title> <title>Password Encoding</title>
<para> <para>
The password package of the spring-security-crypto module provides support for encoding passwords. The password package of the spring-security-crypto module provides support for encoding passwords.
PasswordEncoder is the central service interface and has the following signature: <interfacename>PasswordEncoder</interfacename> is the central service interface and has the following signature:
<programlisting language="java"><![CDATA[ <programlisting language="java"><![CDATA[
public interface PasswordEncoder { public interface PasswordEncoder {
String encode(String rawPassword); String encode(String rawPassword);
@ -120,7 +120,7 @@ public interface PasswordEncoder {
This method is designed to support password-based authentication schemes. This method is designed to support password-based authentication schemes.
</para> </para>
<para> <para>
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 <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:
</para> </para>
<programlisting language="java"><![CDATA[ <programlisting language="java"><![CDATA[
StandardPasswordEncoder encoder = new StandardPasswordEncoder("secret"); StandardPasswordEncoder encoder = new StandardPasswordEncoder("secret");