Let's use the 'testpass' again:
htdigest -c .htpasswd_test testrealm testuser
( src )
AuthType in .htaccess changed:
AuthUserFile /home/i/web/.htpasswd_test AuthName "testrealm" AuthType Digest <Limit GET POST> require valid-user </Limit>
Contents of .htpasswd_test:
testuser:testrealm:ebff225e1ceb73e026fcc645af3e84f6
In fact, this is:
% echo -ne testuser:testrealm:testpass | md5sum ebff225e1ceb73e026fcc645af3e84f6 -
Now try to access that web page, the server may return:
HTTP/1.1 401 Unauthorized ... WWW-Authenticate: Digest realm="testrealm", nonce="sgIhliA3BgA=93e0dd849ec95fa55d43d0797c2cdfb0c2a2be78", algorithm=MD5, qop="auth" ...
And what my web browser sends:
GET /test HTTP/1.1 Host: ... ... Authorization: Digest username="testuser", realm="testrealm", nonce="sgIhliA3BgA=93e0dd849ec95fa55d43d0797c2cdfb0c2a2be78", uri="/test", algorithm=MD5, response="d8c2a49300fd548a4b91cee9d285198f", qop=auth, nc=00000001, cnonce="4fcb938b78ad9041"
This is a bit more complex protocol, explained in Wikipedia.
See the code for calculating response: client.py.
Ouch... but how a server knows password, if it is MD5-hashed? Yes, but server knows MD5(user || realm || password), which is a part of that protocol.
Thus password cannot be wiretapped during MITM attack.
See: server.py.
However, another interesting problem:
A server can store HA1 = MD5(username:realm:password) instead of the password itself. However, if the stored HA1 is leaked, an attacker can generate valid responses and access documents in the realm just as easily as if they had access to the password itself. The table of HA1 values must therefore be protected as securely as a file containing plaintext passwords.[12]
( Wikipedia )
Now how to crack the exchange using hashcat. The 'sip' mode can be used, because auth in SIP was almost the same as here. (Inherited from HTTP. See RFC3261.)
The full string format:
$sip$***[username]*[realm]*GET*[uri_protocol]*[uri_ip]*[uri_port]*[nonce]*[clientNonce]*[nonceCount]*[qop]*MD5*[response]
But uri_protocol and uri_port is to be omitted.
The hashcat string:
$sip$***user*realm*method**uri**nonce*nonceCount*cnonce*auth*MD5*response
In our case, this is:
$sip$***testuser*testrealm*GET**/test**sgIhliA3BgA=93e0dd849ec95fa55d43d0797c2cdfb0c2a2be78*4fcb938b78ad9041*00000001*auth*MD5*d8c2a49300fd548a4b91cee9d285198f
Put it to for-hashcat and:
% hashcat -m 11400 for-hashcat
(Tune it with -a option...)
There are two nonces used -- from server ('nonce') and from client ('cnonce'). As a protection against replay attacks.
MITM attacker can't provoke any side to leak any important information.
This scheme would be still robust, if not to use outdated and cracked MD5. But the current Apache 2.4.63 still uses MD5.
If you still need to use HTTP auth (maybe for compatibility/legacy reasons), at least upgrade to TLS and both basic/digest auth would be OK.

Some time ago (before 24-Mar-2025) there was Disqus JS script for comments. I dropped it --- it was so motley, distracting, animated, with too much ads. I never liked it. Also, comments didn"t appeared correctly (Disqus was buggy). Also, my blog is too chamberlike --- not many people write comments here. So I decided to switch to the model I once had at least in 2020 --- send me your comments by email (don"t forget to include URL to this blog post) and I"ll copy&paste it here manually.
Let"s party like it"s ~1993-1996, in this ultimate, radical and uncompromisingly primitive pre-web1.0-style blog and website.