RFC 2069

An Extension to HTTP : Digest Access Authentication

๐Ÿ“… published January 1997, from ietf

Introduction โœจ

Who, what, when ๐ŸŽ€

This doc is from the Network Working Group, and the lineup is iconic: J. Franks from Northwestern University, P. Hallam-Baker from CERN, J. Hostetler and E. Sink from Spyglass, Inc., P. Leach from Microsoft Corporation, A. Luotonen from Netscape Communications Corporation, and L. Stewart from Open Market, Inc. ๐Ÿ’… It's RFC 2069, it's on the Standards Track, it was published in January 1997, and it's titled "An Extension to HTTP : Digest Access Authentication." The header doesn't list anything it obsoletes or updates, so there's no drama there. ๐ŸŒธ

Status of This Memo ๐Ÿ“œ

This is an Internet standards track protocol for the Internet community, and the authors are asking for discussion and suggestions for improvements ๐Ÿซถ If you want to know where it stands in the standardization process, check the current edition of the "Internet Official Protocol Standards" (STD 1). Distribution is unlimited, so share away! ๐Ÿฆ‹

Abstract ๐ŸŒˆ

So "HTTP/1.0" has a Basic Access Authentication scheme, and honestly? Not a secure move. The user name and password get sent over the network as clear text ๐Ÿ˜ญ That's the big problem, and it needs a new scheme to fix it. Enter Digest Access Authentication โœจ Like Basic, Digest checks that both sides know a shared secret (a password), but the big glow-up is that the password never has to go over the wire in the clear. ๐Ÿ’– And just like most auth protocols, the real risks usually live in the policies and procedures around the thing, not in the core protocol itself ๐Ÿ™ˆ

Table of Contents

   INTRODUCTION......................................................  2
    1.1  PURPOSE ....................................................  2
    1.2  OVERALL OPERATION ..........................................  3
    1.3  REPRESENTATION OF DIGEST VALUES ............................  3
    1.4  LIMITATIONS ................................................  3
   2. DIGEST ACCESS AUTHENTICATION SCHEME............................  3
    2.1 SPECIFICATION OF DIGEST HEADERS .............................  3
     2.1.1 THE WWW-AUTHENTICATE RESPONSE HEADER .....................  4
     2.1.2 THE AUTHORIZATION REQUEST HEADER .........................  6
     2.1.3 THE AUTHENTICATION-INFO HEADER ...........................  9
    2.2 DIGEST OPERATION ............................................ 10
    2.3 SECURITY PROTOCOL NEGOTIATION ............................... 10
    2.4 EXAMPLE ..................................................... 11
    2.5 PROXY-AUTHENTICATION AND PROXY-AUTHORIZATION ................ 11
   3. SECURITY CONSIDERATIONS........................................ 12
    3.1 COMPARISON WITH BASIC AUTHENTICATION ........................ 13
    3.2 REPLAY ATTACKS .............................................. 13
    3.3 MAN IN THE MIDDLE ........................................... 14
    3.4 SPOOFING BY COUNTERFEIT SERVERS ............................. 15
    3.5 STORING PASSWORDS ........................................... 15
    3.6 SUMMARY ..................................................... 16
   4.  ACKNOWLEDGMENTS............................................... 16
   5. REFERENCES..................................................... 16
   6. AUTHORS' ADDRESSES............................................. 17

Introduction โœจ

1.1. Purpose ๐Ÿ’ญ

So "HTTP/1.0" includes a Basic Access Authentication scheme [1]. Basic isn't considered secure, because the user name and password are passed over the network in an unencrypted form ๐Ÿ˜ฌ Future HTTP versions need a new authentication scheme, and this document is that scheme, called "Digest Access Authentication" ๐ŸŽ€

Digest is not the full answer to web security. It doesn't encrypt object content at all. The goal is just a weak access authentication method that dodges the worst flaws of Basic ๐Ÿซถ

It's also proposed that this access authentication scheme be included in the proposed HTTP/1.1 specification ๐Ÿงš

1.2. Overall Operation ๐Ÿฆ‹

Digest is based on the same simple challenge-response idea as Basic. The server challenges with a nonce value, and a valid response has a checksum (by default the MD5 checksum) of the username, the password, the given nonce value, the HTTP method, and the requested URI. So the password is never sent in the clear ๐Ÿ” Just like Basic, the username and password need to be set up ahead of time somehow, and this document doesn't cover how ๐Ÿ“

1.3. Representation of digest values ๐ŸŒธ

A header is optional, and it lets the server specify the algorithm used to create the checksum or digest. By default, that's MD5, and MD5 is the only algorithm described in this document ๐Ÿ’œ

Here's the formatting tea โ˜• An MD5 digest is 128 bits, and it gets represented as 32 ASCII printable characters. You convert the bits from most significant to least significant, four bits at a time, into their ASCII version. Each group of four bits becomes its hexadecimal character from 0123456789abcdef. So binary 0000 is '0', 0001 is '1', and it keeps going up to 1111 being 'f' โœจ

1.4. Limitations ๐Ÿ™ˆ

Real talk: this scheme has a lot of known limitations. It's meant as a replacement for basic authentication and nothing more ๐Ÿ’€

It's a password-based system, and on the server side it has all the same problems any password system has. Specifically, this protocol doesn't handle the initial secure setup between user and server to establish the user's password ๐Ÿ”‘

Users and implementors should know this protocol isn't as secure as Kerberos, and it isn't as secure as any client-side private-key scheme either. BUT it's still better than nothing, better than what people commonly use with telnet and ftp, and better than Basic authentication ๐Ÿซถ Not perfect, but it's a glow-up ๐Ÿ’…


2. Digest Access Authentication Scheme ๐Ÿ‘‘

2.1. Specification of Digest Headers ๐Ÿ“

Digest is conceptually similar to Basic. The modified WWW-Authenticate header line and the Authorization header line are specified below, using the extended BNF defined in the HTTP/1.1 specification, section 2.1. On top of that, there's a brand new header called Authentication-info ๐ŸŽ€

2.1.1 The WWW-Authenticate Response Header โœจ

okay so bestie, this is the server going "hold up, who even are you? ๐Ÿ’…" ๐ŸŽ€ If a server gets a request for an access-protected object and there's no acceptable Authorization header, it fires back a "401 Unauthorized" status code plus a WWW-Authenticate header ๐Ÿฆ‹ Here's the layout:

     WWW-Authenticate    = "WWW-Authenticate" ":" "Digest"
                              digest-challenge

     digest-challenge    = 1#( realm | [ domain ] | nonce |
                          [ digest-opaque ] |[ stale ] | [ algorithm ] )

     realm               = "realm" "=" realm-value
     realm-value         = quoted-string
     domain              = "domain" "=" <"> 1#URI <">
     nonce               = "nonce" "=" nonce-value
     nonce-value         = quoted-string
     opaque              = "opaque" "=" quoted-string
     stale               = "stale" "=" ( "true" | "false" )
     algorithm           = "algorithm" "=" ( "MD5" | token )

Now let's talk about what each parameter actually means ๐ŸŒธ

realm This is the string that gets shown to users so they know which username and password to use ๐Ÿ”‘ It should contain at least the name of the host doing the authentication, and it might also say which group of users can get in. The example they give is "registered_users@gotham.news.com" ๐Ÿฆ‡ The realm is a "quoted-string" as specified in section 2.2 of the HTTP/1.1 specification [2].

domain This is a comma-separated list of URIs, the same way it works for HTTP/1.0 ๐ŸŒˆ The idea is that the client can use it to figure out the set of URIs that should get the same authentication info. The URIs in this list can live on different servers, which is kinda iconic โœจ If this keyword is left out or empty, the client should assume the domain is every URI on the responding server.

nonce This is a server-specified data string that may be uniquely generated every single time a 401 response goes out ๐ŸŽฒ It's recommended that this string be base64 or hexadecimal data. Since the string gets passed in the header lines as a quoted string, the double-quote character is not allowed, so no sneaking those in ๐Ÿ™ˆ

The contents of the nonce are implementation dependent, and the quality of the implementation depends on making a good choice here ๐Ÿ’– A recommended nonce would include:

        H(client-IP ":" time-stamp ":" private-key )

where client-IP is the dotted quad IP address of the client making the request, time-stamp is a server-generated time value, and private-key is data known only to the server ๐Ÿ” With a nonce of this form, the server would normally recalculate the nonce after receiving the client's authentication header, and it would reject the request if the result didn't match the nonce from that header. This is how the server can limit reuse of a nonce to the IP address it was issued to, and also limit how long the nonce stays valid โฐ More on the rationale for building nonces is in section 3.2 below.

An implementation might choose not to accept a previously used nonce or a previously used digest, to protect against a replay attack ๐Ÿ›ก๏ธ Or it might use one-time nonces or digests for POST or PUT requests, and a time-stamp for GET requests. For more details on the issues involved, see section 3. of this document ๐Ÿ“–

The nonce is opaque to the client ๐Ÿซถ

opaque This is a string of data, specified by the server, that the client should send back unchanged ๐ŸŽ It's recommended that this string be base64 or hexadecimal data. This field is a "quoted-string" as specified in section 2.2 of the HTTP/1.1 specification [2].

stale This is a flag that says the client's previous request was rejected because the nonce value was stale ๐Ÿฅ€ If stale is TRUE (in upper or lower case), the client may just want to retry the request with a new encrypted response, without making the user type in a new username and password again ๐Ÿ™Œ The server should only set stale to true if it gets a request where the nonce is invalid but the digest for that nonce is valid. That combo tells the server the client knows the correct username and password ๐Ÿ”ฅ

algorithm This is a string naming a pair of algorithms: one used to produce the digest and one used to produce a checksum ๐Ÿงฎ If it's not present, it's assumed to be "MD5". In this document, the string you get by applying the digest algorithm to the data "data" with the secret "secret" is written as KD(secret, data), and the string you get by applying the checksum algorithm to the data "data" is written as H(data) ๐Ÿ’œ

For the "MD5" algorithm:

        H(data) = MD5(data)

and

        KD(secret, data) = H(concat(secret, ":", data))

i.e., the digest is the MD5 of the secret, concatenated with a colon, concatenated with the data ๐Ÿ’…

2.1.2 The Authorization Request Header ๐Ÿฆ‹

The client is expected to retry the request, this time passing an Authorization header line, which is defined as follows ๐ŸŽ€

Authorization       = "Authorization" ":" "Digest" digest-response

digest-response     = 1#( username | realm | nonce | digest-uri |
                         response | [ digest ] | [ algorithm ] |
                         opaque )

username            = "username" "=" username-value
username-value      = quoted-string
digest-uri          = "uri" "=" digest-uri-value
digest-uri-value    = request-uri         ; As specified by HTTP/1.1
response            = "response" "=" response-digest
digest             = "digest" "=" entity-digest

response-digest     = <"> *LHEX <">
entity-digest      = <"> *LHEX <">
LHEX                = "0" | "1" | "2" | "3" | "4" | "5" | "6" | "7" |
                      "8" | "9" | "a" | "b" | "c" | "d" | "e" | "f"

The definitions of response-digest and entity-digest above show the encoding for their values. The following definitions show how the value gets computed ๐Ÿง 

     response-digest     =
          <"> < KD ( H(A1), unquoted nonce-value ":" H(A2) > <">

     A1             = unquoted username-value ":" unquoted realm-value
                                                ":" password
     password       = < user's password >
     A2             = Method ":" digest-uri-value

The "username-value" field is a "quoted-string" as specified in section 2.2 of the HTTP/1.1 specification [2]. However, the surrounding quotation marks are removed when forming the string A1 โœ‚๏ธ So if the Authorization header includes these fields:

    username="Mufasa", realm="myhost@testrealm.com"

and the user Mufasa has the password "CircleOfLife", then H(A1) would be H(Mufasa:myhost@testrealm.com:CircleOfLife), with no quotation marks in the digested string ๐Ÿ‘‘

No white space is allowed in any of the strings the digest function H() is applied to, unless that white space exists in the quoted strings or entity body that make up the string being digested ๐Ÿ™…โ€โ™€๏ธ For example, the string A1 in the illustration above must be Mufasa:myhost@testrealm.com:CircleOfLife with no white space on either side of the colons. Likewise, the other strings digested by H() must not have white space on either side of the colons that delimit their fields, unless that white space was in the quoted strings or entity body being digested.

"Method" is the HTTP request method as specified in section 5.1 of [2]. The "request-uri" value is the Request-URI from the request line, as specified in section 5.1 of [2] ๐Ÿ“œ It may be "*", an "absoluteURL" or an "abs_path" as specified in section 5.1.2 of [2], but it MUST agree with the Request-URI. In particular, it MUST be an "absoluteURL" if the Request-URI is an "absoluteURL" ๐Ÿ”—

The authenticating server must make sure that the document designated by the "uri" parameter is the same as the document it serves ๐Ÿ•ต๏ธโ€โ™€๏ธ Duplicating information from the request URL in this field is there to deal with the possibility that an intermediate proxy alters the client's request. That altered (but presumably semantically equivalent) request would not produce the same digest as the one the client calculated ๐Ÿ™ƒ

The optional "digest" field contains a digest of the entity body and some of the associated entity headers ๐Ÿ“ This digest can be useful in both request and response transactions. In a request, it can help insure the integrity of POST data or data being PUT to the server. In a response, it insures the integrity of the served document ๐Ÿ’– The value of the "digest" field is an <entity-digest>, defined as follows:

entity-digest = <"> KD (H(A1), unquoted nonce-value ":" Method ":"
                           date ":" entity-info ":" H(entity-body)) <">
       ; format is <"> *LHEX <">

date = = rfc1123-date            ; see section 3.3.1 of [2]
entity-info = H(
          digest-uri-value ":"
          media-type ":"         ; Content-type, see section 3.7 of [2]
          *DIGIT ":"             ; Content length, see 10.12 of [2]
          content-coding ":"     ; Content-encoding, see 3.5 of [2]
          last-modified ":"      ; last modified date, see 10.25 of [2]
          expires                ; expiration date; see 10.19 of [2]
          )

last-modified   = rfc1123-date  ; see section 3.3.1 of [2]
expires         = rfc1123-date

The entity-info elements bring in the values of the URI used to request the entity, plus the associated entity headers Content-type, Content-length, Content-encoding, Last-modified, and Expires ๐ŸŽจ These headers are all end-to-end headers (see section 13.5.1 of [2]) that must not be modified by proxy caches. The "entity-body" is as specified by section 10.13 of [2] or RFC 1864 ๐Ÿ“ฆ

Note that not every entity will have an associated URI or all of these headers ๐Ÿฅบ For example, an entity that is the data of a POST request will typically not have a digest-uri-value, or Last-modified or Expires headers. If an entity doesn't have a digest-uri-value or a header matching one of the entity-info fields, that field is left empty in the computation of entity-info. All the colons specified above are still present, though ๐Ÿ’œ For example, the entity-info for POST data with content-type "text/plain", no content-encoding, and a length of 255 bytes would be:

H(:text/plain:255:::)

Similarly, a request may not have a "Date" header. In that case, the date field of the entity-digest should be empty ๐ŸŒ™

In the entity-info and entity-digest computations, there must be no white space between "words" and "tspecials", and exactly one blank between "words", except for the blank after the comma in "rfc1123-date" (see section 2.2 of [2]) ๐Ÿ“

Implementors should be aware of how authenticated transactions interact with proxy caches ๐Ÿšจ The HTTP/1.1 protocol specifies that when a shared cache (see section 13.10 of [2]) has received a request containing an Authorization header and a response from relaying that request, it MUST NOT return that response as a reply to any other request, unless one of two Cache-control (see section 14.9 of [2]) directives was present in the response. If the original response included the "must-revalidate" Cache-control directive, the cache MAY use the entity of that response in replying to a subsequent request, but it MUST first revalidate it with the origin server, using the request headers from the new request so the origin server can authenticate the new request ๐Ÿ” Alternatively, if the original response included the "public" Cache-control directive, the response entity MAY be returned in reply to any subsequent request ๐ŸŒˆ

2.1.3 The AuthenticationInfo Header โœจ

okay so when authentication SUCCEEDS ๐Ÿฅณ, the Server MAY optionally send an Authentication-info header to say something useful, like an entity digest or a brand new nonce for the next round. it has two fields, digest and nextnonce, and BOTH are optional bestie ๐Ÿ’…

AuthenticationInfo = "Authentication-info" ":"
                                  1#( digest | nextnonce )

nextnonce      = "nextnonce" "=" nonce-value

digest = "digest" "=" entity-digest

the digest field lets the client check that the response body didn't get messed with on the way over ๐Ÿ•ต๏ธโ€โ™€๏ธ the server would probably only send this when it actually has the document and can compute the digest. it would probably skip this header for CGI output, tbh ๐Ÿ™ˆ the value of "digest" is an <entity-digest>, computed the way we talked about above ๐Ÿงš

the nextnonce field is the nonce the server wants the client to use for the NEXT authentication response. remember either field is optional!! the server can send the Authentication-info header with ONLY nextnonce, which is how it does one-time nonces ๐Ÿฆ‹ and if nextnonce shows up, the client is strongly encouraged to use it in the next WWW-Authenticate header. if the client doesn't, the server might ask it to re-authenticate with stale=TRUE. ngl that's a whole plot twist ๐Ÿ˜ญ


2.2 Digest Operation ๐Ÿ’…

when the server gets an Authorization header, it MAY check that it's valid by looking up the known password for the submitted username ๐Ÿ” then the server MUST run the same MD5 operation the client ran and compare the result to the given response-digest. it's giving detective energy fr ๐Ÿ•ต๏ธโ€โ™€๏ธ

the HTTP server doesn't actually need to know the user's clear text password ๐Ÿคซ as long as H(A1) is available to the server, the validity of an Authorization header MAY be verified. iconic, honestly ๐Ÿ‘‘

a client MAY remember the username, password and nonce values, so future requests inside the specified <domain> can include the Authorization header preemptively ๐Ÿš€ the server MAY accept the old Authorization header info even if the nonce isn't fresh anymore. or it could send back a 401 with a new nonce, and the client retries the request. when the server adds stale=TRUE to that response, it's basically saying "retry with the new nonce, no need to bug the user for a username and password again" ๐Ÿ’–

the opaque data is for carrying state info around ๐ŸŽ€ example: a server might be in charge of authenticating content that actually lives on a different server. the first 401 response could include a domain field with the URI on that second server, plus the opaque field for state info. the client retries, and then the server MAY reply with a 301/302 redirect pointing at the URI on the second server. the client follows the redirect and passes the same Authorization header, including the <opaque> data the second server might need โœจ

just like with the basic scheme, proxies MUST be completely transparent in the Digest access authentication scheme ๐Ÿซถ meaning they MUST forward the WWW-Authenticate, Authentication-info and Authorization headers untouched. if a proxy wants to authenticate a client BEFORE the request goes to the server, it can do that with the Proxy-Authenticate and Proxy-Authorization headers from section 2.5 below ๐ŸŒธ


2.3 Security Protocol Negotiation ๐Ÿฆ‹

it's really useful for a server to know which security schemes a client can handle ๐Ÿ‘€

if this proposal gets accepted as a required part of the HTTP/1.1 specification, a server MAY assume Digest support when a client says it's HTTP/1.1 compliant โœ…

it's possible a server might want to REQUIRE Digest as its authentication method, even if it doesn't know the client supports it ๐Ÿ˜ฌ a client is encouraged to fail gracefully if the server asks for any authentication scheme it can't handle. graceful failing is a whole aesthetic ๐Ÿ’…


2.4 Example ๐Ÿ“

this example assumes someone is requesting an access-protected document ๐Ÿ“„ the URI of the document is http://www.nowhere.org/dir/index.html. both client and server know the username for this document is "Mufasa" and the password is "CircleOfLife" ๐Ÿ‘‘ (the lion king reference is not an accident, periodt ๐Ÿฆ)

the first time the client asks for the document, there's no Authorization header, so the server responds with:

HTTP/1.1 401 Unauthorized
WWW-Authenticate: Digest    realm="testrealm@host.com",
                            nonce="dcd98b7102dd2f0e8b11d0f600bfb0c093",
                            opaque="5ccc069c403ebaf9f0171e9517f40e41"

the client MAY prompt the user for the username and password. after that it sends a new request with this Authorization header:

Authorization: Digest       username="Mufasa",
                            realm="testrealm@host.com",
                            nonce="dcd98b7102dd2f0e8b11d0f600bfb0c093",
                            uri="/dir/index.html",
                            response="e966c932a9242554e42c8ee200cec7f6",
                            opaque="5ccc069c403ebaf9f0171e9517f40e41"

2.5 Proxy-Authentication and Proxy-Authorization ๐ŸŒˆ

the digest scheme MAY also be used to authenticate users to proxies, proxies to proxies, or proxies to end servers, using the Proxy-Authenticate and Proxy-Authorization headers ๐Ÿงš these are instances of the general Proxy-Authenticate and Proxy-Authorization headers from sections 10.30 and 10.31 of the HTTP/1.1 specification [2], and their behavior is subject to the restrictions described there ๐Ÿ“š the proxy auth transactions are basically the same vibe as the ones we already covered ๐Ÿ’–

upon receiving a request that needs authentication, the proxy/server MUST issue the "HTTP/1.1 401 Unauthorized" header, followed by a "Proxy-Authenticate" header of this form:

  Proxy-Authentication     = "Proxy-Authentication" ":" "Digest"
                                digest-challenge

where digest-challenge is as defined above in section 2.1. the client/proxy MUST then re-issue the request with a Proxy-Authenticate header of this form:

  Proxy-Authorization      = "Proxy-Authorization" ":"
                                digest-response

where digest-response is as defined above in section 2.1. when authentication succeeds, the Server MAY optionally provide a Proxy-Authentication-info header of this form:

Proxy-Authentication-info = "Proxy-Authentication-info" ":" nextnonce

where nextnonce has the same semantics as the nextnonce field in the Authentication-info header described above in section 2.1 โœจ

here's a plot twist ๐Ÿ™ˆ a client could be asked to authenticate itself to BOTH a proxy AND an end-server. it might get an "HTTP/1.1 401 Unauthorized" header followed by both a WWW-Authenticate and a Proxy-Authenticate header. however, it can NEVER receive more than one Proxy-Authenticate header, since those headers are only for immediate connections and MUST NOT be passed on by proxies ๐Ÿ›‘ if the client gets both headers, it MUST respond with both the Authorization and Proxy-Authorization headers as described above, which will likely involve different combos of username, password, nonce, etc. it's a lot, but we can handle it ๐Ÿ’ช


3. Security Considerations ๐Ÿ”ฅ

okay real talk bestie, Digest Authentication does NOT provide a strong authentication mechanism ๐Ÿ˜ญ that was never the goal. it's meant to replace something much weaker and even more dangerous: Basic Authentication ๐Ÿ’€ an important design constraint was that the new scheme be free of patent and export restrictions ๐ŸŒ

most needs for secure HTTP transactions can't be met by Digest Authentication ๐Ÿšซ for those, SSL or SHTTP are more appropriate protocols. digest authentication can't be used for any transaction that requires encrypted content. still, there are plenty of functions where digest authentication is both useful and appropriate ๐Ÿ’œ

3.1 Comparison with Basic Authentication ๐Ÿ™ˆ

both Digest and Basic Authentication are very much on the weak end of the security strength spectrum ๐Ÿ˜ฌ but comparing them shows why it's useful, even necessary, to replace Basic with Digest.

the biggest threat to these kinds of transactions is network snooping ๐Ÿ‘€ for example, online access to a database that's only for paying subscribers. with Basic authentication, an eavesdropper can get the user's password ๐Ÿ˜ฑ that lets them into everything in the database, and, honestly worse, into anything else the user protects with the same password. it's a whole disaster ๐Ÿ’€

with Digest Authentication, the eavesdropper only gets access to the transaction in question, NOT the user's password ๐Ÿซถ the info they gain WOULD allow a replay attack, but only with a request for the same document, and even that might be difficult ๐Ÿงš security is giving "one-hit wonder" energy and I'm here for it โœจ

3.2. Replay Attacks ๐Ÿ”

Okay so a replay attack against digest auth is usually pointless for a simple GET request ๐Ÿ™„ Like, the eavesdropper already saw the only doc they could get with a replay, because the URI of the requested doc gets digested into the client response and the server will only deliver that doc. Compare that to Basic Authentication, where once the eavesdropper has the user's password, every doc protected by it is open to them ๐Ÿ’€ A GET with form data could only be "replayed" with the identical data. Still, it could be a problem if it made a CGI script do something on the server ๐Ÿ‘€

So for some purposes, you really do need to protect against replay attacks ๐Ÿ›ก๏ธ A good digest implementation can do that in a few different ways. The server-created nonce value is implementation dependent, but if it contains a digest of the client IP, a time-stamp, and a private server key (as recommended above), then a replay attack is not simple. The attacker has to convince the server that the request is coming from a false IP address, and has to make the server deliver the doc to an IP address that's different from the one the server thinks it's sending to. The attack can only work in the period before the time-stamp expires. Digesting the client IP and time-stamp into the nonce means you can have an implementation that doesn't keep state between transactions, which is so clean โœจ

For apps where no possibility of replay attack can be tolerated, the server can use one-time response digests that won't be honored for a second use. This means the server has to remember which digests have been used until the nonce time-stamp (and so the digest built with it) has expired, which is extra overhead, but it effectively protects against replays ๐Ÿ’ช Instead of keeping a list of the values of used digests, a server would hash these values and require re-authentication whenever a hash collision occurs.

โš ๏ธ POST and PUT requests need special attention! A successful replay could result in counterfeit form data or a counterfeit version of a PUT file. The use of one-time digests or one-time nonces is recommended. It is also recommended that the optional <digest> be implemented for use with POST or PUT requests to assure the integrity of the posted data. Alternatively, a server may choose to allow digest authentication only with GET requests. Responsible server implementors will document the risks described here as they pertain to a given implementation ๐Ÿซถ

3.3. Man in the Middle ๐Ÿ•ต๏ธโ€โ™€๏ธ

Both Basic and Digest authentication are vulnerable to "man in the middle" attacks, for example from a hostile or compromised proxy. Obviously this brings all the problems of eavesdropping, but it could also give the attacker some extra threats ๐Ÿ˜ค

A simple but effective attack would be to replace the Digest challenge with a Basic challenge, which tricks the client into revealing their password ๐Ÿ’€ To protect against this, clients should remember if a site has used Digest authentication in the past, and warn the user if the site stops using it. It might also be a good idea for the browser to be configured to demand Digest authentication in general, or from specific sites ๐Ÿ”’

Or, a hostile proxy might spoof the client into making a request the attacker wanted instead of one the client wanted. Of course, this is still much harder than a comparable attack against Basic Authentication ๐Ÿ’…

There are several attacks on the "digest" field in the Authentication-info header. A simple but effective one is just removing the field, so the client can't use it to detect modifications to the response entity. Sensitive applications may wish to allow configuration that requires the digest field to be present when appropriate. More subtly, the attacker can alter any of the entity-headers that aren't incorporated in the computation of the digest. The attacker can alter most of the request headers in the client's request, and can alter any response header in the origin-server's reply, except those headers whose values are incorporated into the "digest" field ๐Ÿง

Alteration of Accept* or User-Agent request headers can only result in a denial of service attack that returns content in an unacceptable media type or language ๐Ÿ™ƒ Alteration of cache control headers can also only result in denial of service. Alteration of Host will be detected, if the full URL is in the response-digest ๐ŸŽฏ Alteration of Referer or From is not important, as these are only hints ๐Ÿซ 

3.4. Spoofing by Counterfeit Servers ๐ŸŽญ

Basic Authentication is vulnerable to spoofing by counterfeit servers ๐Ÿ˜ฌ If a user can be led to believe she's connecting to a host containing information protected by a password she knows, when in fact she's connecting to a hostile server, then the hostile server can request a password, store it away for later use, and feign an error. This type of attack is more difficult with Digest Authentication, but the client must know to demand that Digest authentication be used, perhaps using some of the techniques described above to counter "man-in-the-middle" attacks ๐Ÿ”

3.5. Storing passwords ๐Ÿ—๏ธ

Digest authentication requires that the authenticating agent (usually the server) store some data derived from the user's name and password in a "password file" associated with a given realm. Normally this might contain pairs consisting of username and H(A1), where H(A1) is the digested value of the username, realm, and password as described above ๐Ÿ“

The security implications of this are that if this password file is compromised, an attacker gains immediate access to documents on the server using this realm ๐Ÿ˜ฑ Unlike, say, a standard UNIX password file, this information doesn't need to be decrypted in order to access documents in the server realm associated with this file. On the other hand, decryption (or, more likely, a brute force attack) would be necessary to obtain the user's password. This is the reason the realm is part of the digested data stored in the password file. It means that if one digest authentication password file is compromised, it does not automatically compromise others with the same username and password (though it does expose them to brute force attack) ๐Ÿง 

There are two important security consequences of this. First, the password file must be protected as if it contained unencrypted passwords, because for the purpose of accessing documents in its realm, it effectively does ๐Ÿšจ

A second consequence is that the realm string should be unique among all realms which any single user is likely to use. In particular, a realm string should include the name of the host doing the authentication. The inability of the client to authenticate the server is a weakness of Digest Authentication ๐Ÿฅฒ

3.6. Summary ๐Ÿ’œ

By modern cryptographic standards, Digest Authentication is weak. But for a large range of purposes it's valuable as a replacement for Basic Authentication ๐ŸŒธ It remedies many, but not all, weaknesses of Basic Authentication. Its strength may vary depending on the implementation. In particular, the structure of the nonce (which depends on the server implementation) may affect the ease of mounting a replay attack. A range of server options is appropriate, since for example some implementations may be willing to accept the server overhead of one-time nonces or digests to eliminate the possibility of replay, while others may be satisfied with a nonce like the one recommended above, restricted to a single IP address and with a limited lifetime ๐Ÿฆ‹

๐Ÿ‘‘ The bottom line is that any compliant implementation will be relatively weak by cryptographic standards, but any compliant implementation will be far superior to Basic Authentication. Iconic. Periodt. ๐ŸŒˆ

4. Acknowledgments ๐Ÿซถ

In addition to the authors, valuable discussion instrumental in creating this document has come from Peter J. Churchyard, Ned Freed, and David M. Kristol ๐Ÿ’–

5. References ๐Ÿ“š

[1] Berners-Lee, T., Fielding, R., and H. Frystyk, "Hypertext Transfer Protocol -- HTTP/1.0", RFC 1945, May 1996.

[2] Berners-Lee, T., Fielding, R., and H. Frystyk, "Hypertext Transfer Protocol -- HTTP/1.1" RFC 2068, January 1997.

[3] Rivest, R., "The MD5 Message-Digest Algorithm", RFC 1321, April 1992.

6. Authors' Addresses ๐ŸŽ€

John Franks
Professor of Mathematics
Department of Mathematics
Northwestern University
Evanston, IL 60208-2730, USA

EMail: john@math.nwu.edu
Phillip M. Hallam-Baker
European Union Fellow
CERN
Geneva
Switzerland

EMail: hallam@w3.org
Jeffery L. Hostetler
Senior Software Engineer
Spyglass, Inc.
3200 Farber Drive
Champaign, IL  61821, USA

EMail: jeff@spyglass.com
Paul J. Leach
Microsoft Corporation
1 Microsoft Way
Redmond, WA 98052, USA

EMail: paulle@microsoft.com
Ari Luotonen
Member of Technical Staff
Netscape Communications Corporation
501 East Middlefield Road
Mountain View, CA 94043, USA

EMail: luotonen@netscape.com
Eric W. Sink
Senior Software Engineer
Spyglass, Inc.
3200 Farber Drive
Champaign, IL  61821, USA

EMail: eric@spyglass.com
Lawrence C. Stewart
Open Market, Inc.
215 First Street
Cambridge, MA  02142, USA

EMail: stewart@OpenMarket.com
๐Ÿ“ฆ where this page comes from