Digest Authentication
Current proposal:
draft-ietf-http-digest-aa-02.txt
- Adding an algorithm parameter.
- Describe in detail construction of nonces.
Here there are a number of tricks already in use which ensure that
a nonce is only valid for requests comming from a single TCP/IP
address.
- Enhance 'security considerations' section to explain limitations.
- Fix dependence on 'extension mechanism'.
-
The gross structure of the digests allows for the
exploitation of MD5 collisions. This is possibly not worth
worrying about, since the best attack we can come up with
requires effort on the order of 2^64 operations. In general,
the sharing of long common prefixes between the digests and the
lack of secret or random material beyond the initial amount
leads us to suspect that there might be many other
cryptoanalytic attacks we haven't thought of.
- The fine structure of the digests allows one to be substituted
for another. This allows for straightforward splicing and
reflection attacks which undercut the rationale for the
protocol. This could be fixed by insisting that each digest
type have some sort of type-distinguishing data or structure in
them (there are three specified in the document: client plain
digests, client "message-digests" [sic], and server
"message-digests" [sic again]). Vulnerability to substitution
is increased given the one-sided and unstructured nature of the
freshness material. One could easily arrange that the client
always provided freshness material, and insist that freshness
have structure that the either side can count on (say that it
must monotonically increase).
- The "optional-ness" of the client message-digest and server
message-digests means that neither can be used for
authentication given a downgrade attack (the attacker removes
the digest and substitutes unauthenticated material).
- The fact that no headers are included in the digesting process
combined with the fact that HTTP headers change the semantics
of requests (and replies) means that authenticated requests and
replies can be transformed by an attacker undetectably. For
example, consider byte ranges where the authorized request or
only wants one portion of a document and the attacker
transforms the request into one for the entire document. This
is difficult to fix while retaining the spirit of the proposal.
- There is no treatment of the security implications of retries
and multiple authorization headers. Absent this, I can imagine
many flawed implementation possibilities. Also, I think that it
is assumed that this mechanism works for proxy authentication,
and if this is permitted, new sorts of attacks are possible.
Outside of these immediate security vulnerabilities, I wonder about
the wisdom of wiring-in a single digest algorithm (the selection of
MD5 could easily be parameterized with no damage to the spec).
I also wonder about the wisdom of referencing Dave Kristol's
extension mechanism (sounds like what used to be called at PARC "error
33" -- making one risky project dependent on another).
Given the above, here's an off-the-top-of-my-head attempt at addressing
these vulnerabilities, while retaining as much spirit of the design as
possible. It is an admittedly bad practice I am indulging in here -- this
is not a thought-out design, it's only meant to illustrate fixes.
-
The nonces are mandatory, and have the following structure:
<host-id><sep1><tod><sep2><integer>
host-id is the principal's DNS name or the "realm", I don't
care. tod is seconds since Unix epoch in hex. discrim
is a hex integer so that multiple nonces generated in a given second
monotonically increase. I don't care what sep1 and sep2
are (slashes?). This is so the principals can check for replay with
finite memory. Clients have nonces too.
- The client auth header is:
Authorization: Digest
algorithm=MD5,
username="<username>",
realm="<realm>",
snonce="<server-nonce>",
cnonce="<client-nonce>", -- must be fresh
uri="<requested-uri>",
request="<client-digest>",
message="<message-digest>",
opaque="<opaque>" -- required if provided by server
where:
<client-digest> := H( H(A1) + CN + SN + BI + H(H(A1) + A2) )
<message-digest> := H( H(A1) + <client-digest> + H(H(A1) + CB) )
and:
A1 := 'MD5' + U + R + P
A2 := <Method> + <requested-uri>
BI := "cbody" | "no cbody" -- depending
OP := "opaque" + <opaque> | "no opaque" -- depending
CB := "cbody" + <message-body> | "no cbody" -- depending
with:
SN, CN -- server and client nonce values
U -- username
R -- realm
P -- password
<Method> -- entire request header line 0
<requested-uri> -- uri sans proxy/routing
- server response
When authorization succeeds, the Server MUST provide the following:
HTTP/1.1 200 OK
Authorization-Response: Digest
algorithm=MD5,
username="<username>",
realm="<realm>",
snonce="<server-nonce>",
cnonce="<client-nonce>",
response="<server-digest>"
where:
<server-digest> := H( H(A1) + CD + SB + H(H(A1) + <Response>))
and with:
A1 as above
CD := <client-digest> -- from above
SB := "sbody" + <message-body> | "no sbody" -- depending
<Response> -- entire response header line 0
- This mechanism must be outlawed for "Proxy-Authentication:" or
it we need to make the structure of A1 dependent on proxy vs.
non-proxy use.
- The headers that change the effect of a request or response such as:
Range, Unless, If-Modified-Since
(I'm worried that there are others) must either be outlawed
when using Digest-Authentication, or these headers must be
accounted-for in the digests. Figuring-out which are the
headers that have this property is a surprisingly hard problem.
- Multiple Authorization headers are forbidden.
- Servers must either disregard the request line 0 URI (in favor
of the uri field of the authorization header) or reject
requests where these are not identical. Even better would be to
drop the uri field from the authorization header.
As a firewall developer I see the Digest Access Authentication mechanism a
useful construct. I would like to see some additional but compatible
functionality added to the proposal to do with proxy-authentication.
I would like to see an explicit definition as how it may be used with
proxy-authenticate. Proxy authenticate currently does not handle nestsed
firewalls very well since the first proxy should strip out the proxy-auth
stuff (:-<
With the addition of an authentication point parameter, a proxy could then
strip only the proxy-auth lines that are applicable to it. This would allow
nested authentication.
One drawback of nested authentication is the shuttling of requests back and
forward between client and proxies. This is best seen if you consider what
happens if the proxies don't allow re-use.
client -> proxy proxy says 407 proxy-auth...
client ->proxy->server
proxy happy, but server wants auth as well.
client ->proxy proxy says 407 again since previous auth is nolonger
valid.
client-proxy->server
client finally gets data.
A simple scheme to get around this is to allow servers and proxies to
piggyback the next challenge to the current response..
This is purely an optimisation but makes the whole process work.