Thursday, January 27, 2011

How does Kerberos actually work in the HTTP world?

I got hit with an IM out of the blue this morning that kicked off a bunch of conversations about how Kerberos works with HTTP. As I was working my way around the org it became clear that lots of people say "and Kerberos happens here" and move on, but most didn't have a good understanding of how it actually worked "on the wire".

If you want my description read on. If you want to see more authoritative info you should check out Wikipedia's page on SPNEGO.

First a quick lesson in HTTP to make sure everyone's on the same page. Sorry if you know this already... When your browser (or anything really) makes an HTTP call it sends a message like this:

GET /path/file.html HTTP/1.0
followed by a blank line. The server responds in kind:
HTTP/1.1 200 OK
Date: Thu, 27 Jan 2011 17:21:03 GMT
Content-Length: 680
Connection: close
Content-Type: text/html; charset=UTF-8
followed by a blank line and then the content. In this case 680 bytes of HTML. The first line begins with the four letters HTTP (to tell you that it's HTTP) followed by a slash and the version number of the protocol that the server supports. Immediately after that is a numeric code called that indicates whether the result is "everything's OK", a redirect or an error of some sort. After that comes a "human readable" string that matches up with that status code number.

If the server wants you to authenticate with a username and password it can ask your browser to pop up that ugly grey box by demanding what are called "Basic Credentials". To do that it responds with the status code 401 and an additional HTTP header that says "WWW-Authenticate" with the value Basic and a friendly name to show in the box. So something like this:

HTTP/1.1 401 Authorization Required
Date: Sat, 27 Nov 2004 10:18:15 GMT
WWW-Authenticate: Basic realm="Super secret stuff"
Content-Type: text/html
Content-Length: 482
Your browser will show an ugly grey box something like this:

The idea in the spec is that if you hit cancel you should see the 482 bytes of HTML that the server sent (not all browsers do this, some show you a "friendly" page instead. If you type your username and password and hit OK your browser will repeat the original request, but this time it will add on your credentials to that request. The resulting request looks something like this:
GET /path/file.html HTTP/1.0
Authorization: Basic Y2hyaXM6bm90cmVhbGx5bXlwYXNzd29yZA==

Basic tells the server that the credentials immediately following are of type "Basic" and then the actual credentials are sent. The HTTP spec says that for Basic credentials you take the username plus a colon plus the password and Base64 encode them.
Incidentally in Apache and most other servers the REMOTE_USER header is populated by the server automatically with whatever is before the colon in that block of text above.
OK, now on to the actual info you came to this post to read!

When the server wants to do Kerberos it has to tell your browser that it supports Kerberos. Back in 2006 (I feel so old) Microsoft implemented a mechanism to do Kerberos between Internet Explorer (IE) and Internet Information Server (IIS) and documented it in an RFC. The spec extends the "WWW-Authenticate" with a new type of token called "Negotiate" and the semantics of how you're supposed to use that to to Kerberos. I'll spare you reading through a boring RFC and distill it down to an example:

HTTP/1.1 401 Authorization Required
Date: Sat, 27 Nov 2004 10:18:15 GMT
WWW-Authenticate: Negotiate [token]
Content-Type: text/html
Content-Length: 482
[token] is actually a base64 encoded block of data that you're supposed to look at and use to get your Kerberos Service Ticket. Your browser is supposed to respond with something like
GET /path/file.html HTTP/1.0
Authorization: Negotiate [response]

And the [response] is your Service Ticket (and some other junk).

All clear?

OK, one last little detail and I promise I'll stop.

When you make your first HTTP call the server has no idea whether your browser knows how to get a Service Ticket or even if you know how to deal with the Negotiate header at all. Generally this isn't a big deal because the server would only send that header if you accessed something that's protected and configured to request Kerberos tokens. And the HTTP spec actually allows you to say "send me a Negotiate token OR a Basic token"; it even goes so far as to say that the client needs to order the authentication methods so that it will try the "more secure" mechanism first - or in other words that a Negotiate token is preferred to a Basic token (for example).

This all can lead to some interesting behaviors if your client doesn't know how to handle WWW-Authenticate set to "Negotiate", if your client doesn't know what to do if it gets more than one WWW-Authenticate HTTP header in the response. The latter is more common than you'd think!

And there's another interesting story about how WebLogic's SPNEGO Identity Asserter hooks into all of this. But that's a story for another time. ;-)


  1. Hi Chris,

    Thanks for the wonderful post.

    Do you think we should clarify the priority of the authentication types "Negotiate", "NTLM", "Digest", "Basic" in case server responds with more than one WWW-Authenticate. If I am not wrong, clients(ex: chrome), by default, enforce a priority order of authentication type irrespective of the header responses from server.


  2. The HTTP spec says that browsers are supposed to try them in "most secure to least secure" order. RFC 2617 says:
    " An HTTP/1.1 server may return multiple challenges with a 401
    (Authenticate) response, and each challenge may use a different
    auth-scheme. A user agent MUST choose to use the strongest auth-
    scheme it understands and request credentials from the user based
    upon that challenge.

    Servers should (will?) always order them from most secure to least secure anyway, but if they don't then the browser will also apply its own ordering.

    Or at least that's how it's supposed to work. :-)


Note: Only a member of this blog may post a comment.