123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169 |
- <chapter xmlns="http://docbook.org/ns/docbook" version="5.0" xml:id="basic">
- <info>
- <title>Basic and Digest Authentication</title>
- </info>
- <para>Basic and digest authentiation are alternative authentication mechanisms which are popular
- in web applications. Basic authentication is often used with stateless clients which pass
- their credentials on each request. It's quite common to use it in combination with
- form-based authentication where an application is used through both a browser-based user
- interface and as a web-service. However, basic authentication transmits the password as
- plain text so it should only really be used over an encrypted transport layer such as
- HTTPS.</para>
- <section xml:id="basic-processing-filter">
- <info>
- <title><classname>BasicAuthenticationFilter</classname></title>
- </info>
- <para><literal>BasicAuthenticationFilter</literal> is responsible for processing basic
- authentication credentials presented in HTTP headers. This can be used for
- authenticating calls made by Spring remoting protocols (such as Hessian and Burlap), as
- well as normal browser user agents (such as Firefox and Internet Explorer). The standard
- governing HTTP Basic Authentication is defined by RFC 1945, Section 11, and
- <literal>BasicAuthenticationFilter</literal> conforms with this RFC. Basic
- Authentication is an attractive approach to authentication, because it is very widely
- deployed in user agents and implementation is extremely simple (it's just a Base64
- encoding of the username:password, specified in an HTTP header).</para>
- <section xml:id="basic-config">
- <info>
- <title>Configuration</title>
- </info>
- <para>To implement HTTP Basic Authentication, you need to add a
- <literal>BasicAuthenticationFilter</literal> to your filter chain. The application
- context should contain <literal>BasicAuthenticationFilter</literal> and its required
- collaborator:</para>
- <para> <programlisting language="xml"><![CDATA[
- <bean id="basicAuthenticationFilter"
- class="org.springframework.security.web.authentication.www.BasicAuthenticationFilter">
- <property name="authenticationManager" ref="authenticationManager"/>
- <property name="authenticationEntryPoint" ref="authenticationEntryPoint"/>
- </bean>
- <bean id="authenticationEntryPoint"
- class="org.springframework.security.web.authentication.www.BasicAuthenticationEntryPoint">
- <property name="realmName" value="Name Of Your Realm"/>
- </bean>]]>
- </programlisting> </para>
- <para>The configured <interfacename>AuthenticationManager</interfacename> processes each
- authentication request. If authentication fails, the configured
- <interfacename>AuthenticationEntryPoint</interfacename> will be used to retry the
- authentication process. Usually you will use the filter in combination with a
- <literal>BasicAuthenticationEntryPoint</literal>, which returns a 401 response with
- a suitable header to retry HTTP Basic authentication. If authentication is
- successful, the resulting <interfacename>Authentication</interfacename> object will
- be placed into the <classname>SecurityContextHolder</classname> as usual.</para>
- <para>If the authentication event was successful, or authentication was not attempted
- because the HTTP header did not contain a supported authentication request, the
- filter chain will continue as normal. The only time the filter chain will be
- interrupted is if authentication fails and the
- <interfacename>AuthenticationEntryPoint</interfacename> is called.</para>
- </section>
- </section>
- <section xml:id="digest-processing-filter">
- <title><classname>DigestAuthenticationFilter</classname></title>
- <para><classname>DigestAuthenticationFilter</classname> is capable of processing digest
- authentication credentials presented in HTTP headers. Digest Authentication attempts to
- solve many of the weaknesses of Basic authentication, specifically by ensuring
- credentials are never sent in clear text across the wire. Many user agents support
- Digest Authentication, including FireFox and Internet Explorer. The standard governing
- HTTP Digest Authentication is defined by RFC 2617, which updates an earlier version of
- the Digest Authentication standard prescribed by RFC 2069. Most user agents implement
- RFC 2617. Spring Security's <classname>DigestAuthenticationFilter</classname> is
- compatible with the "<literal>auth</literal>" quality of protection
- (<literal>qop</literal>) prescribed by RFC 2617, which also provides backward
- compatibility with RFC 2069. Digest Authentication is a more attractive option if you
- need to use unencrypted HTTP (i.e. no TLS/HTTPS) and wish to maximise security of the
- authentication process. Indeed Digest Authentication is a mandatory requirement for the
- WebDAV protocol, as noted by RFC 2518 Section 17.1.</para>
- <para>Digest Authentication is definitely the most secure choice between Form
- Authentication, Basic Authentication and Digest Authentication, although extra security
- also means more complex user agent implementations. Central to Digest Authentication is
- a "nonce". This is a value the server generates. Spring Security's nonce adopts the
- following format:</para>
- <para>
- <programlisting language="txt">
- base64(expirationTime + ":" + md5Hex(expirationTime + ":" + key))
- expirationTime: The date and time when the nonce expires, expressed in milliseconds
- key: A private key to prevent modification of the nonce token
- </programlisting> </para>
- <para>The <classname>DigestAuthenticatonEntryPoint</classname> has a property specifying the
- <literal>key</literal> used for generating the nonce tokens, along with a
- <literal>nonceValiditySeconds</literal> property for determining the expiration time
- (default 300, which equals five minutes). Whist ever the nonce is valid, the digest is
- computed by concatenating various strings including the username, password, nonce, URI
- being requested, a client-generated nonce (merely a random value which the user agent
- generates each request), the realm name etc, then performing an MD5 hash. Both the
- server and user agent perform this digest computation, resulting in different hash codes
- if they disagree on an included value (eg password). In Spring Security implementation,
- if the server-generated nonce has merely expired (but the digest was otherwise valid),
- the <classname>DigestAuthenticationEntryPoint</classname> will send a
- <literal>"stale=true"</literal> header. This tells the user agent there is no need to
- disturb the user (as the password and username etc is correct), but simply to try again
- using a new nonce.</para>
- <para>An appropriate value for <classname>DigestAuthenticationEntryPoint</classname>'s
- <literal>nonceValiditySeconds</literal> parameter will depend on your application.
- Extremely secure applications should note that an intercepted authentication header can
- be used to impersonate the principal until the <literal>expirationTime</literal>
- contained in the nonce is reached. This is the key principle when selecting an
- appropriate setting, but it would be unusual for immensely secure applications to not be
- running over TLS/HTTPS in the first instance.</para>
- <para>Because of the more complex implementation of Digest Authentication, there are often
- user agent issues. For example, Internet Explorer fails to present an
- "<literal>opaque</literal>" token on subsequent requests in the same session. Spring
- Security filters therefore encapsulate all state information into the
- "<literal>nonce</literal>" token instead. In our testing, Spring Security's
- implementation works reliably with FireFox and Internet Explorer, correctly handling
- nonce timeouts etc.</para>
- <section xml:id="digest-config">
- <title>Configuration</title>
- <para>Now that we've reviewed the theory, let's see how to use it. To implement HTTP
- Digest Authentication, it is necessary to define
- <literal>DigestAuthenticationFilter</literal> in the filter chain. The application
- context will need to define the <literal>DigestAuthenticationFilter</literal> and
- its required collaborators:</para>
- <para> <programlisting language="xml"><![CDATA[
- <bean id="digestFilter" class=
- "org.springframework.security.web.authentication.www.DigestAuthenticationFilter">
- <property name="userDetailsService" ref="jdbcDaoImpl"/>
- <property name="authenticationEntryPoint" ref="digestEntryPoint"/>
- <property name="userCache" ref="userCache"/>
- </bean>
- <bean id="digestEntryPoint" class=
- "org.springframework.security.web.authentication.www.DigestAuthenticationEntryPoint">
- <property name="realmName" value="Contacts Realm via Digest Authentication"/>
- <property name="key" value="acegi"/>
- <property name="nonceValiditySeconds" value="10"/>
- </bean>]]>
- </programlisting> </para>
- <para>The configured <interfacename>UserDetailsService</interfacename> is needed because
- <literal>DigestAuthenticationFilter</literal> must have direct access to the clear
- text password of a user. Digest Authentication will NOT work if you are using
- encoded passwords in your DAO <footnote><para>It is possible to encode the password in the
- format HEX( MD5(username:realm:password) ) provided the
- <code>DigestAuthenticationFilter.passwordAlreadyEncoded</code> is set to <code>true</code>.
- However, other password encodings will not work with digest authentication.</para></footnote>.
- The DAO collaborator, along with the <literal>UserCache</literal>, are typically shared directly
- with a <classname>DaoAuthenticationProvider</classname>. The
- <literal>authenticationEntryPoint</literal> property must be
- <classname>DigestAuthenticationEntryPoint</classname>, so that
- <classname>DigestAuthenticationFilter</classname> can obtain the correct
- <literal>realmName</literal> and <literal>key</literal> for digest
- calculations.</para>
- <para>Like <literal>BasicAuthenticationFilter</literal>, if authentication is successful
- an <interfacename>Authentication</interfacename> request token will be placed into
- the <classname>SecurityContextHolder</classname>. If the authentication event was
- successful, or authentication was not attempted because the HTTP header did not
- contain a Digest Authentication request, the filter chain will continue as normal.
- The only time the filter chain will be interrupted is if authentication fails and
- the <interfacename>AuthenticationEntryPoint</interfacename> is called, as discussed
- in the previous paragraph.</para>
- <para>Digest Authentication's RFC offers a range of additional features to further
- increase security. For example, the nonce can be changed on every request. Despite
- this, Spring Security implementation was designed to minimise the complexity of the
- implementation (and the doubtless user agent incompatibilities that would emerge),
- and avoid needing to store server-side state. You are invited to review RFC 2617 if
- you wish to explore these features in more detail. As far as we are aware, Spring
- Security's implementation does comply with the minimum standards of this RFC.</para>
- </section>
- </section>
- </chapter>
|