SEC-1689: Minor doc updates related to use of password encoding and the crypto package.
This commit is contained in:
parent
e470eaa41d
commit
1dc309b041
|
@ -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>
|
||||||
|
|
|
@ -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");
|
||||||
|
|
Loading…
Reference in New Issue