Understanding HTTP Digest Access Authentication

Digest Access Authentication is one method that a client and server can use to exchange credentials over HTTP. This method uses a combination of the password and other bits of information to create an MD5 hash which is then sent to the server to authenticate. Sending a hash avoids the problems with sending a password in clear text, a shortfall of Basic Access Authentication.

Digest Access was originally defined in RFC 2069, and optional security enhancements were later added in RFC 2617 which should be considered the current standard if you wish to implement this method yourself. We’ll have a look at both in this article.

The Problem with Basic Access Authentication

First, lets look a Basic Access Authentication. With your browser, you request some sort of object that requires you to authenticate yourself. If the browser hasn’t cached credentials you’ve already provided, you’ll be met with a “HTTP 401 Not Authorized” response which includes the following header:

WWW-Authenticate: Basic realm="example.com"

The client/browser is then expected to respond with the appropriate credentials in order to be allowed access.

The username and password are concatenated together, like “admin:p@ssw0rd”, This string is then Base64 encoded. The encoded string (YWRtaW46cEBzc3cwcmQ=) is used in an Authorization header which is sent back to the server.

Authorization: Basic YWRtaW46cEBzc3cwcmQ=

When the server receives this request, it notices the Authorization header and applies the steps in reverse (decode and split the resulting string) to get the original username and password. The process can then do whatever lookup/verification is necessary with the credentials.

The major problem here is that Base64 is a data transfer scheme; it’s not a hashing or encryption scheme. It may as well be clear text. Digest Access Authentication tries to improve upon this by sending the password as a hashed value, which is much harder (not impossible) to reverse engineer a clear text value from.

Working with Digest Access Authentication

When working with Digest Access Authentication, it’s the server who must make the first move when it discovers a client is trying to access a restricted area. There are a few values the server needs to provide as part of the WWW-Authenticate header that the client needs.

The server generates a value, referred to as nonce, which should be unique for each request.
It’s recommended this value be either Base64 or hexadecimal, specifically because it will be enclosed in double quotes and we want to ensure there are no double quotes in the string. The nonce will be used by the client to generate a hash to send back to the server.

In PHP, applying md5() to a unique string is generally sufficient.

<?php
$nonce = md5(uniqid());

The next value needed by the client is opaque. This is another unique string generated by the server and is expected to be sent and returned by the client unaltered.

<?php
$opaque = md5(uniqid());

The last value, realm, is just a string to display to users so they know which username and password they should provide. It’s also used by the client to generate a hash to send back to the server.

<?php
$realm = 'Authorized users of example.com';

All of these values are used to compose the WWW-Authenticate directive and send as a response to the client.

<?php
if (empty($_SERVER['PHP_AUTH_DIGEST']) {
    header('HTTP/1.1 401 Unauthorized');
    header(sprintf('WWW-Authenticate: Digest realm="%s", nonce="%s", opaque="%s"', $realm, $nonce, $opaque));
    header('Content-Type: text/html');
    echo '<p>You need to authenticate.</p>';
    exit;
}

When the client receives this response, it has to compute a return hash. It does so by concatenating the username, realm, and password and hashing the result with MD5 as follows:

  1. Compute A1 as MD5("username:realm:password").
  2. Compute A2 as MD5("requestMethod:requestURI").
  3. Compute the final hash, know as “response”, as MD5("A1:nonce:A2").

The client sends the response back to the server in an Authorization header and includes the username, realm, nonce, opaque, uri, and the computed response.

Authorization: Digest username="%s", realm="%s", nonce="%s", opaque="%s", uri="%s", response="%s"'

Note that realm, nonce, and opaque are all returned to the server unchanged.

When the server receives the response, the same steps are taken to compute the server’s version of the hash. If the computed hash and the received response hash values match, then the request is considered authorized. It looks something like this:

<?php
$A1 = md5("$username:$realm:$password");
$A2 = md5($_SERVER['REQUEST_METHOD'] . ":$uri");
$response = md5("$A1:$nonce:$A2");

Of course, the user’s password is never passed to the server. To perform authentication look ups from a database then, the value of A1 can be stored. Keep in mind however the stored hash becomes invalid if you ever change your realm.

Improving on the Original Digest Access Spec

Now that you’re familiar with the workings of Digest Access Authentication from RFC 2069, let’s turn our attention to some of the enhancements that were added in 2617: qop, nc, and cnonce.

qop, or quality of protection, is specified in the WWW-Authenticate header and can have a value of “auth” or “auth-int”. When no qop directive is found or when it is set to “auth”, Digest Access is used for client authentication only – the default mode which you’ve seen so far. When set to “auth-int”, an attempt is made to provide some level of integrity protection of the response as well and the client must also include the request body as part of the message digest. This allows the server to determine whether the request has been adulterated in transfer between the indented client and intended server.

The client nonce, or cnonce, is similar to nonce but is generated by the client. The cnonce figures into the response digest computed by the client and its original value is passed along to the server so that it can be used there to compare digests. This provides some response integrity and mutual authentication, in that both the client and server have a way to prove they know a shared secret. When the qop directive is sent by the server, the client must include a cnonce value.

The nonce count, nc, is a hexadecimal count of the number of requests that the client has sent with a given nonce value. This way, the server can guard against replay attacks.

With the enhancements, the computation of A2 and the response goes something like this:

<?php
if ($qop == 'auth-int') {
    $A2 = md5($_SERVER['REQUEST_METHOD'] . ":$uri:" . md5($respBody));
}
else {
    $A2 = md5($_SERVER['REQUEST_METHOD'] . ":$uri");
}
$response = md5("$A1:$nonce:$nc:$cnonce:$qop:$A2");

Strengths and Weaknesses of Digest Access Authentication

Digest Access has some advantages over Basic Authentication, since Basic Authentication uses a clear-text exchange of username and passwords, which is almost the same as telling the world what your password is. Digest Access passes a hashed value and not the password itself, so it’s much more secure than Basic Auth. The server nonce, which should be unique per request, will drastically change the computed hash on each new request, and the nc value provided by RFC 2617 is helpful at preventing replay attacks, whereby a malicious individual is able to intercept your request data and “replay” or repeat it as his own request.

There are a few weaknesses with Digest Authentication as well. When RFC 2617 replaced the original specification, the enhancements that were added to provide extra security measures between the client and server were made purely optional, and Digest Authentication will proceed in its original RFC 2069 form when not implemented.

Another problem is MD5 is not a strong hashing algorithm. All it takes is time and CPU to brute force the original value back to life. Bcrypt is preferable as it is more resilient against brute force attacks. There’s also no way for a server to verify the identity of the requesting client when using Digest Access Authentication. This opens the possibility for man in the middle attacks, where a client can be led to believe a given server is really who he thinks it is, but ends up sending his login credentials to an unknown entity.

Your best bet when dealing with authentication is to use SSL and encrypt passwords using Bcrypt. You can use Basic Authentication or a home brewed authentication mechanism over SSL, but for situations where SSL is not possible for whatever reason, Digest Access is better than simple Basic Authentication and sending passwords in plain text over the public Internet.

There’s a simple example of RFC-2069 HTTP Digest Access available on GitHub to accompany this article. If you’re the type of person who likes to explore and tinker, feel free to clone it. A good place to start your exercise is to make the necessary modifications to work with accounts stored in a database instead of the hard-coded credentials in the script (remember, you’ll need to calculate and store A1 as the user’s password hash for later lookup at login), and then enhance it with RFC-2617 features. Afterwards, you’ll have a pretty solid understanding of HTTP Digest Access Authentication.

Summary

By now you should have an idea of what your available HTTP authentication methods are.

Basic Authentication is the easiest to implement and also the least secure. Usernames and passwords are encoded in Base64 but effectively sent to the server in plain text.

Digest Authentication improves on the Basic method by sending authentication data through MD5. While MD5 is still not a strong password encryption algorithm, it is much harder to decode than the plain text/Base64 method of Basic Authentication. In its original form, there were still problems of man in the middle attacks and not being able to confirm server identity, but these weaknesses have been improved in later revisions to the RFC.

SSL is the most modern and secure method of sending user authentication data over the public Internet. But when SSL is not available, please use Digest over Basic authentication.

Image via Fotolia

Free book: Jump Start HTML5 Basics

Grab a free copy of one our latest ebooks! Packed with hints and tips on HTML5's most powerful new features.

  • Great tutorial

    Now I can use it instead basic auth. Thank you!