Digest authentication is another authentication type specified in HTTP 1.1. Unlike basic authentication, digest authentication does not require the password to be transmitted. The Digest scheme challenges using a nonce value. A valid response contains a checksum (by default, the MD5 checksum) of the username, the password, the given nonce value, the HTTP method, and the requested URI. Just as with the Basic scheme, the username and password must be prearranged.

Introduction

Digest Authentication, used both by SIP and HTTP, introduces the ability to only save an encrypted version of the password on the server. This prevents the client from sending the password in an easily decodable format, and it allows the server to save a hash of the password (which cannot be easily decoded).

Digest Authentication Flow

Let’s examine in detail the messages exchanged between the client and server:

  1. A client requests a page that requires authentication. But usually we don’t send username and password here. This is because the client does not know whether the page requires authentication or not.
  2. The server returns a 401 response code and informs the client about the authentication realm, qop, nonce, opaque (digest authentication).
  3. The client then presents the user with an authentication realm (usually a brief description of the computer or system being accessed) and prompts for a username and password. The user can also cancel here.
  4. After the user enters a username and password, the client adds an authorization header which includes username, realm, nonce, uri, qop,nc, cnonce, response, opaque to the request and resends it.
  5. If authentication is successful, the server will process requests for pages that require authentication.
    1. The MD5 hash of the combined username, authentication realm, and password is calculated. The result is referred to as HA1.
    2. The MD5 hash of the combined method and digest URI is calculated (for example, of “GET” and “/dir/index.html”). The result is referred to as HA2.
    3. The MD5 hash of the combined HA1 result, server nonce (nonce), request counter (nc), client nonce (cnonce), quality of protection code (qop), and HA2 result is calculated. The result is the “response” value provided by the client. if the username or password is incorrect, the server will return a 401 response code again. The client then again prompts the user for a username and password.

PHP Example Code

<?php
    $realm = 'Restricted area';

    //user => password
    $users = array('admin' => 'mypass', 'guest' => 'guest');


    if (empty($_SERVER['PHP_AUTH_DIGEST'])) {
        header('HTTP/1.1 401 Unauthorized');
        header('WWW-Authenticate: Digest realm="'.$realm.
            '",qop="auth",nonce="'.uniqid().'",opaque="'.md5($realm).'"');

        die('Text to send if user hits Cancel button');
    }


    // analyze the PHP_AUTH_DIGEST variable
    if (!($data = http_digest_parse($_SERVER['PHP_AUTH_DIGEST'])) ||
        !isset($users[$data['username']]))
        die('Wrong Credentials!');


    // generate the valid response
    $A1 = md5($data['username'] . ':' . $realm . ':' . $users[$data['username']]);
    $A2 = md5($_SERVER['REQUEST_METHOD'].':'.$data['uri']);
    $valid_response = md5($A1.':'.$data['nonce'].':'.$data['nc'].':'.$data['cnonce'].':'.$data['qop'].':'.$A2);

    if ($data['response'] != $valid_response)
        die('Wrong Credentials!');

    // ok, valid username & password
    echo 'You are logged in as: ' . $data['username'];


    // function to parse the http auth header
    function http_digest_parse($txt)
    {
        // protect against missing data
        $needed_parts = array('nonce'=>1, 'nc'=>1, 'cnonce'=>1, 'qop'=>1, 'username'=>1, 'uri'=>1, 'response'=>1);
        $data = array();
        $keys = implode('|', array_keys($needed_parts));

        preg_match_all('@(' . $keys . ')=(?:([\'"])([^\2]+?)\2|([^\s,]+))@', $txt, $matches, PREG_SET_ORDER);

        foreach ($matches as $m) {
            $data[$m[1]] = $m[3] ? $m[3] : $m[4];
            unset($needed_parts[$m[1]]);
        }

        return $needed_parts ? false : $data;
    }
?>
  1. Request without authentication :

     Request URL: http://localhost/auth/index.php
     Request Method: GET
     Status Code: 401 Unauthorized
     Remote Address: [::1]:80
     Referrer Policy: strict-origin-when-cross-origin
    
  2. Server response indicating that authentication is required :

     Connection: Keep-Alive
     Content-Length: 39
     Content-Type: text/html; charset=UTF-8
     Date: Mon, 05 Sep 2022 14:05:24 GMT
     Keep-Alive: timeout=5, max=100
     Server: Apache/2.4.48 (Win64) OpenSSL/1.1.1l PHP/7.4.23
     WWW-Authenticate: Basic realm="MyRealm"
     X-Powered-By: PHP/7.4.23
    

    basic_auth

  3. Presented by client and entered by user.
  4. Request with authentication (username “root”, password “password”): basic_auth

     Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
     Accept-Encoding: gzip, deflate, br
     Accept-Language: en-US,en;q=0.9,ja;q=0.8,zh-CN;q=0.7,zh;q=0.6
     Authorization: Basic YWRtaW46ZGFtaW4=
     Cache-Control: no-cache
     Connection: keep-alive
     Host: localhost
     Pragma: no-cache
     sec-ch-ua: "Google Chrome";v="105", "Not)A;Brand";v="8", "Chromium";v="105"
     sec-ch-ua-mobile: ?0
     sec-ch-ua-platform: "Windows"
     Sec-Fetch-Dest: document
     Sec-Fetch-Mode: navigate
     Sec-Fetch-Site: none
     Sec-Fetch-User: ?1
     Upgrade-Insecure-Requests: 1
     User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/105.0.0.0 Safari/537.36
    
  5. Server response

     HTTP / 1.1  200  OK 
     Connection: Keep-Alive
     Content-Length: 61
     Content-Type: text/html; charset=UTF-8
     Date: Mon, 05 Sep 2022 14:13:10 GMT
     Keep-Alive: timeout=5, max=100
     Server: Apache/2.4.48 (Win64) OpenSSL/1.1.1l PHP/7.4.23
     X-Powered-By: PHP/7.4.23
    

    image info

Advantages of Digst Auth

  1. The password is not used directly in the digest, but rather HA1 = MD5 (username:realm:password). This allows some implementations (such as JBoss to store HA1 rather than the plaintext password.
  2. Client nonce was introduced in RFC 2617, which allows the client to prevent chosen-plaintext attacks, such as rainbow tables, that could otherwise threaten digest authentication schemes.
  3. Server nonce is allowed to contain timestamps, meaning the server may inspect nonce attributes submitted by clients to prevent replay attacks. Server is also allowed to maintain a list of recently issued or used server nonce values to prevent reuse.

Disadvantages of Digst Auth

  1. Many of the security options in RFC 2617 are optional. If quality-of-protection (QOP) is not specified by the server, the client will operate in a security-reduced legacy RFC 2069 mode
  2. Digest access authentication is vulnerable to a man-in-the-middle (MitM) attack. For example, a MitM attacker could tell clients to use basic access authentication or legacy RFC2069 digest access authentication mode. To extend this further, digest access authentication provides no mechanism for clients to verify the server’s identity
  3. Some servers require passwords to be stored using reversible encryption. However, it is possible to instead store the digested value of the username, realm, and password.
  4. It prevents the use of a strong password hash (such as bcrypt) when storing passwords (since either the password or the digested username, realm, and password must be recoverable).

Conclusion

In conclusion, Digest is an improvement, but it still has significant drawbacks. The main one being that it suffers from man-in-the-middle attacks. To make Digest secure, the connection between client and server must be encrypted and the server encryption key preloaded, or a certificate authority must be used to allow the client to verify the server’s public key.