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>
<section xml:id="core-services-erasing-credentials">
<title>Erasing Credentials on Successful Authentication</title>
<para>
By default (from Spring Security 3.1 onwards) the <classname>ProviderManager</classname>
will attempt to clear any sensitive credentials information from the
<interfacename>Authentication</interfacename> object which is returned by a successful
authentication request. This prevents information like passwords being retained longer
than necessary.
</para>
<para>
This may cause issues when you are using a cache of user objects, for example, to
improve performance in a stateless application. If the <interfacename>Authentication</interfacename>
contains a reference to an object in the cache (such as a <interfacename>UserDetails</interfacename>
instance) and this has its credentials removed, then it will no longer be possible to authenticate
against the cached value. You need to take this into account if you are using a cache. An obvious
solution is to make a copy of the object first, either in the cache implementation or in
the <interfacename>AuthenticationProvider</interfacename> which creates the returned
<interfacename>Authentication</interfacename> object. Alternatively, you can disable the
<literal>eraseCredentialsAfterAuthentication</literal> property on <classname>ProviderManager</classname>.
See the Javadoc for more information.
<para> By default (from Spring Security 3.1 onwards) the
<classname>ProviderManager</classname> will attempt to clear any sensitive
credentials information from the <interfacename>Authentication</interfacename>
object which is returned by a successful authentication request. This prevents
information like passwords being retained longer than necessary. </para>
<para> This may cause issues when you are using a cache of user objects, for example, to
improve performance in a stateless application. If the
<interfacename>Authentication</interfacename> contains a reference to an object in
the cache (such as a <interfacename>UserDetails</interfacename> instance) and this
has its credentials removed, then it will no longer be possible to authenticate
against the cached value. You need to take this into account if you are using a
cache. An obvious solution is to make a copy of the object first, either in the
cache implementation or in the <interfacename>AuthenticationProvider</interfacename>
which creates the returned <interfacename>Authentication</interfacename> object.
Alternatively, you can disable the
<literal>eraseCredentialsAfterAuthentication</literal> property on
<classname>ProviderManager</classname>. See the Javadoc for more information.
</para>
</section>
<section xml:id="core-services-dao-provider">
@ -107,17 +106,13 @@
<bean id="daoAuthenticationProvider"
class="org.springframework.security.authentication.dao.DaoAuthenticationProvider">
<property name="userDetailsService" ref="inMemoryDaoImpl"/>
<property name="saltSource" ref="saltSource"/>
<property name="passwordEncoder" ref="passwordEncoder"/>
</bean>]]></programlisting> The <interfacename>PasswordEncoder</interfacename> and
<interfacename>SaltSource</interfacename> are optional. A
</bean>]]></programlisting> The <interfacename>PasswordEncoder</interfacename> is optional. A
<interfacename>PasswordEncoder</interfacename> provides encoding and decoding of
passwords presented in the <interfacename>UserDetails</interfacename> object that is
returned from the configured <interfacename>UserDetailsService</interfacename>. A
<interfacename>SaltSource</interfacename> enables the passwords to be populated with
a "salt", which enhances the security of the passwords in the authentication
repository. These will be discussed in more detail <link
xlink:href="#core-services-password-encoding">below</link>. </para>
returned from the configured <interfacename>UserDetailsService</interfacename>. This
will be discussed in more detail <link xlink:href="#core-services-password-encoding"
>below</link>. </para>
</section>
</section>
<section>
@ -129,7 +124,8 @@
<para>
<programlisting language="java">
UserDetails loadUserByUsername(String username) throws UsernameNotFoundException;
</programlisting> </para>
</programlisting>
</para>
<para>The returned <interfacename>UserDetails</interfacename> is an interface that provides
getters that guarantee non-null provision of authentication information such as the
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
probably already created. Returning to <literal>JdbcDaoImpl</literal>, an example
configuration is shown below:</para>
<para> <programlisting language="xml"><![CDATA[
<para>
<programlisting language="xml"><![CDATA[
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="org.hsqldb.jdbcDriver"/>
<property name="url" value="jdbc:hsqldb:hsql://localhost:9001"/>
@ -190,7 +187,8 @@
<bean id="userDetailsService"
class="org.springframework.security.core.userdetails.jdbc.JdbcDaoImpl">
<property name="dataSource" ref="dataSource"/>
</bean> ]]> </programlisting> </para>
</bean> ]]> </programlisting>
</para>
<para>You can use different relational database management systems by modifying the
<literal>DriverManagerDataSource</literal> shown above. You can also use a global
data source obtained from JNDI, as with any other Spring configuration.</para>
@ -219,10 +217,18 @@
</section>
<section xml:id="core-services-password-encoding">
<title>Password Encoding</title>
<para>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.</para>
<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
with either the new or legacy <interfacename>PasswordEncoder</interfacename>
types.</para>
<section>
<title>What is a hash?</title>
<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
<programlisting language="txt">
5f4dcc3b5aa765d61d8327deb882cf99
</programlisting> A hash is
<quote>one-way</quote> in the sense that it is very difficult (effectively
</programlisting>
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
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
@ -254,16 +260,28 @@
<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
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
<interfacename>SaltSource</interfacename> interface which can be used by an
authentication provider to generate a salt value for a particular user. 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>
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
<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>
<title> Hashing and Authentication</title>
<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
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
@ -274,20 +292,13 @@
example, and your application is configured to use Spring Security's
<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
enocoder is using hexadecimal strings (the default)<footnote>
<para>You can configure the encoder to use Base 64 instead of hex by setting the
<literal>encodeHashAsBase64</literal> property to <literal>true</literal>. Check
the Javadoc for <classname>MessageDigestPasswordEncoder</classname> and its
parent classes for more information.</para>
</footnote>. Alternatively your database may 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. 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
encoder is using hexadecimal strings (the default). Alternatively your database may
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>
<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>
</section>
</section>

View File

@ -109,7 +109,7 @@ KeyGenerators.string();]]>
<title>Password Encoding</title>
<para>
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[
public interface PasswordEncoder {
String encode(String rawPassword);
@ -120,7 +120,7 @@ public interface PasswordEncoder {
This method is designed to support password-based authentication schemes.
</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>
<programlisting language="java"><![CDATA[
StandardPasswordEncoder encoder = new StandardPasswordEncoder("secret");