Basic and digest authentication 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.
BasicAuthenticationFilter
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 BasicAuthenticationFilter
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).
To implement HTTP Basic Authentication, you need to add a BasicAuthenticationFilter
to your filter chain. The application context should contain BasicAuthenticationFilter
and its required collaborator:
<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>
The configured AuthenticationManager
processes each authentication request. If authentication fails, the configured AuthenticationEntryPoint
will be used to retry the authentication process. Usually you will use the filter in combination with a BasicAuthenticationEntryPoint
, which returns a 401 response with a suitable header to retry HTTP Basic authentication. If authentication is successful, the resulting Authentication
object will be placed into the SecurityContextHolder
as usual.
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 AuthenticationEntryPoint
is called.
DigestAuthenticationFilter
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 Mozilla 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 DigestAuthenticationFilter
is compatible with the “auth” quality of protection (qop
) 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.
Note | |
---|---|
You should not use Digest in modern applications because it is not considered secure. The most obvious problem is that you must store your passwords in plaintext, encrypted, or an MD5 format. All of these storage formats are considered insecure. Instead, you should use a one way adaptive password hash (i.e. bCrypt, PBKDF2, SCrypt, etc). |
Central to Digest Authentication is a "nonce". This is a value the server generates. Spring Security’s nonce adopts the following format:
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
The DigestAuthenticatonEntryPoint
has a property specifying the key
used for generating the nonce tokens, along with a nonceValiditySeconds
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 DigestAuthenticationEntryPoint
will send a "stale=true"
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.
An appropriate value for the nonceValiditySeconds
parameter of DigestAuthenticationEntryPoint
depends on your application. Extremely secure applications should note that an intercepted authentication header can be used to impersonate the principal until the expirationTime
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.
Because of the more complex implementation of Digest Authentication, there are often user agent issues. For example, Internet Explorer fails to present an “opaque” token on subsequent requests in the same session. Spring Security filters therefore encapsulate all state information into the “nonce” token instead. In our testing, Spring Security’s implementation works reliably with Mozilla Firefox and Internet Explorer, correctly handling nonce timeouts etc.
Now that we’ve reviewed the theory, let’s see how to use it. To implement HTTP Digest Authentication, it is necessary to define DigestAuthenticationFilter
in the filter chain. The application context will need to define the DigestAuthenticationFilter
and its required collaborators:
<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>
The configured UserDetailsService
is needed because DigestAuthenticationFilter
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 [15]. The DAO collaborator, along with the UserCache
, are typically shared directly with a DaoAuthenticationProvider
. The authenticationEntryPoint
property must be DigestAuthenticationEntryPoint
, so that DigestAuthenticationFilter
can obtain the correct realmName
and key
for digest calculations.
Like BasicAuthenticationFilter
, if authentication is successful an Authentication
request token will be placed into the SecurityContextHolder
. 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 AuthenticationEntryPoint
is called, as discussed in the previous paragraph.
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.
[15] It is possible to encode the password in the format HEX( MD5(username:realm:password) ) provided the DigestAuthenticationFilter.passwordAlreadyEncoded
is set to true
. However, other password encodings will not work with digest authentication.