This post is part of a multi-part series. It builds on the first post, where I describe the framework we will use to evaluate authentication schemes. If you have not, it is probably a good idea to read it now (hint).
Here is where we are.
- I. What to look for in an authentication scheme?
- II. Evaluation of standard authentication schemes
- III. Cheatsheet and closing thoughts
II. Evaluation of standard authentication schemes
The exotic HTTP Digest Auth
DISCLAIMER: While there may be some cases where Digest Auth is ideal. I recommend against using it. What follows is a detailed introduction to how Digest Auth works. You should read it to understand why its built up the way it is. However, you may wish to skip the intellectual lecture and jump right into the comparism.
Back in ‘97, on a stormy night, our ancestors were sitting in a room quietly contemplating the effects HTTP Basic Auth had on the web. On the one hand, it finally provided an authentication standard people could rely upon. On the other, it had its clear drawbacks, like sending over the cleartext password with every request.
“‘tis the price of simplicity…” - said a quiet voice.
“Yes… but does it have to be?” - followed another.
And so they set out to build the next authentication standard of the web. The fruit of their labor was HTTP Digest Auth. The idea was to get everyone to either switch to the new scheme or use TLS.
How do you think that worked out?
TLS was very expensive back then, and you can imagine how many developers jumped on the idea to upgrade their systems. Nevertheless, the standard survived and had two further RFCs issued to make it more secure. Today, it is a capable, yet underutilized authentication scheme.
Enough history, let's see this beast in action!
How it works?
HTTP Digest is quite a bit more complicated than Basic. For starters, you cannot merely send a digest authentication header to the server out of the blue. It is just not possible.
Why? - you ask.
The reason is something called a nonce. A nonce is a number used only once. A funny thing to note here is that RFC2069 does not require it to be used only once. This has been corrected in RFC2617 where this is mandated.
Anyway, you need to fetch a nonce from the server before you can issue your first authenticated request. Getting the nonce is rather simple. Just send an unauthenticated request, and the server will respond with something like this:
HTTP/1.1 401 Unauthorized WWW-Authenticate: Digest realm="[email protected]", nonce="fe13119fb084fe8bbf5fe3ab7cc89b3b"
And there it is! All you need to do now is grab that hex and use it to calculate the authentication header for your request. You can either do your homework and read RFC2069 and do it manually (not recommended :), or get smart and use a library to arrive at the following authorization header.
Authorization: Digest username="Alfa", realm="[email protected]", nonce="fe13119fb084fe8bbf5fe3ab7cc89b3b", uri="/site.html", response="3cd47b10fc33e68707e242c8ac0b5f26"
Okay, now you got the idea. Let's see what's in the request.
|username||Nothing fancy here, it is exactly what you expect.|
|realm||This is basically the identifier of the site you are authenticating to.|
|nonce||A "number-used-only-once". It comes from the server and the client needs to send it back unchanged. This actually improves security. More on this later.|
|uri||The target URI of the request.|
|response||This is the "digest". A calculated value making sure everything is fine. The following values are used to calculate this: username, realm, password, nonce, request method, request uri|
This all came from the mentioned RFC. It also has a few other optional parameters, but they are out of scope for our purposes. Feel free to check it out if you can't sleep at night.
The idea behind Digest Auth is that you never send the password over the wire. Instead, you calculate a so-called digest and present that as proof of owning the password.
The party doing the authentication will also calculate this digest, and if it matches the one you sent, you are all good. This, of course, implies that both parties will need to be in possession of all the data required to calculate this digest. Therefore the server needs to store your password in a reversible form (e.g., encrypted). I.e., it cannot simply hash it because its original form is required to calculate the digest.
There is a neat trick which lets the server store hashes with the realm and username used as a salt. This helps, as your password is not directly exposed. But it is still vulnerable to a brute force attack at that point since both your username and the realm is sent in clear text over the wire.
Back to the nonce. If used wisely, it can significantly increase security regarding replay attacks. The nonce is opaque to the client, and the spec allows the server to use any custom construction it wants. More on this under Replay protection.
It is also important to note here to Digest Auth can be used to provide integrity protection for the body of the transaction (request and response). The RFC refers to this as Entity Digest, check it out if you are interested. There is a caveat though; this is completely optional. You can implement it as required on the server side, but browsers cannot be instructed to demand it.
The only goal of HTTP Digest was to provide a more secure alternative to HTTP Basic, and that it did.
2 years later… still on Earth… another RFC
A shiny new RFC2617 emerged to address some issues and add further features. Here is how our authentication header would look.
Authorization: Digest username="Alfa", realm="[email protected]", nonce="fe13119fb084fe8bbf5fe3ab7cc89b3b", uri="/site.html", qop=auth, nc=00000001, cnonce="be33401b", response="6629fae49393a05397450978507c4ef1"
Wow, this looks even more complicated! Let's take a moment to understand what is happening here.
|qop||Quality of Protection. This directive can be used require integrity protection beside authentication. Previously this could only be achieved by custom code on server side. Now it is part of the spec.|
|cnonce||A client generated "number-used-only-once". It is used to protect against the so called choosen-plaintext attack.|
|nc||A hex number indicating the times this specific nonce (not cnonce) was used. The client must increase this number each time it sends a request. This makes replay protection trivial, as the server must only remember the last nonce count used. If a request ever comes in where the nc parameter is lower or equal to the last remembered value, it is a replay.|
None of these parameters is required, making the new scheme backward compatible. If used though, it can substantially increase security. This spec is implemented by most browser today.
As you can see, some problems that could be solved with custom code on server side have now been addressed by the spec, making it even more complicated. One thing you might have noticed in the spec is that the hash function in use is always MD5. Nowadays, MD5 is not considered secure enough for cryptographic purposes. So what now?
You guessed it… Another RFC. It adds the ability to use another hash function as well as some other goodies.
There is just one problem. As of this writing, no browser supports this new spec. Even curl was only recently updated. Firefox has an old ticket open, which predates the RFC.
Long story short, it not widespread enough to use it.
Enough history, how does HTTP Digest Auth compare based on our framework?
Well, this is one of the reasons Digest is not so widely used. It is a lot more complicated than Basic, and the specs give implementations quite some room to move around. It is hard to choose a library which can take advantage of all the modern security measures Digest Auth provides. Compromises will need to be made.
In theory, this scheme can be made secure. However, it probably takes more effort than it is worth. Depending on what features you want to use I would rate the complexity between medium and hard.
Reliance on HTTPS
If used, TLS is highly recommended. As you know, the TLS protocol provides integrity, confidentiality, and replay protection for the whole communication.
Modern implementations will still provide some protection without TLS, like minor confidentiality (username), some integrity protection, and replay countermeasures.
The browser completely handles Digest Auth, i.e. once the user types in his credentials everything happens behind the scenes. The browser will attach the computed Authorization header to every request going to the target domain. Therefore CSRF is a problem, and you need to deal with it. While the scheme has quite a lot of security enhancements over Basic (nonce, cnonce, qop, and the digest itself), these do not protect against CSRF.
Obviously, if you are using it between servers, there is nothing to fear.
It does not provide replay protection out-of-the-box. However, the nonce value can be used in such a way to protect against this attack. For instance, your server could issue one-time nonces, where each nonce can only be used for a single request and then it expires.
The nc parameter also helps in this case, as its sole purpose is to thwart replay attacks.
Request/response integrity can somewhat be protected with modern implementations. Unfortunately, these are quite scarce. You need to choose a library, which implements these features. And given the current adaptation, this may be really hard.
Recommended use cases
The consensus among security experts is to favor Basic Auth with TLS over Digest Auth with TLS. The main reason is simplicity and the fact that Basic Auth requires no storage of the password on the server side.
If you have TLS, which you should, all you would gain from Digest Auth over Basic is a defense in depth measure. It may make sense in some cases, but you should probably opt for a modern signature scheme.
Honestly, I do not recommend using digest authentication due to its low market penetration and too open specs. Browsers are missing the modern implementation and you have better options on the backend, like signatures schemes.
Taking care of your auth scheme
If you ever choose to use Digest Auth, make sure you implement the features you want to rely on. Or, if using a library, take the time to look at its source and make sure it implements the optional parts of the spec. Lots of libraries claim to implement RFC7616, yet they only do the bare minimum.
As you either need to store passwords in a reversible form, or use hashing with the realm and username as the salt. Make sure you have a unique realm to make it harder for attackers to brute force user passwords if your server becomes compromised.
Coming up next
After one of the most exotic authentication standards, we will continue our journey with something that is very common on the web today. Probably nothing can rival its simplicity and “fame”: cookies.
Cookies are used everywhere. They are the de-facto request authentication scheme between a browser and the server. It begs the question.
Are cookies the most secure request authentication mechanism between users and websites? Check out the next post to find out!
Spot a mistake in reasoning? Have a different opinion? Sound off in the comments!