Implementing HTTP Digest AuthenticationLast edited on Aug 15, 2014

Recently, I was trying to add HTTP digest authentication on my Home automation device. The device exposes a REST interface trough a proxy server. My web server is setup like this

Now since my API is exposed to the world by proxying it like that, I wanted to add security by implementing HTTP digest authentication. Whether or not Digest authentication with MD5 is secure or not is a completely different story, but let's assume it is good enough for now. I have a restricted access webpage that I go on to control my home automation device. This web page makes requests to DHAS using javascript. Since I've implemented digest authentication, I now need to put the credentials in the javascript so that the calls made with XMLHttpRequest can succeed. Even though that javascript code will only be served to me, while I am authenticated on the website, I felt uncomfortable to leave a hardcoded username and password in the JS source. So this is what I came up with:

Note that messages sent from JS to DHAS are being proxied by Apache. Therefore, DHAS receives a GET for /insteon/listmodules and not for /dhas/insteon/listmodules

  • use XMLHttpRequest to make a request to DHAS (through the proxy)
  • add a header "X-NeedAuthenticationHack" in the request
  • receive a 401
  • get the "X-WWW-Authenticate" header from the 401 response
  • Make a XMLHttpRequest to the server and send it the "X-WWW-Authenticate" data
  • Server side php script with hardcoded username/password for DHAS solves the challenge and returns the resonse
  • use XMLHttpRequest to make a request to DHAS (through the proxy) and append the response in a "Authorization" header

So basically, I just intercepted the 401 and instead of letting the browser prompt for a username password, I created the response myself. And instead of doing in the JS, I did it on the server, limiting the exposition of the username/password. You may notice my two special X headers. This is because if the server returns a 401 with WWW-Authenticate, the browser will prompt for your credentials. Event if I have a handler defined to get the 401. So when I send my initial request, I set the X-NeedAuthenticationHack header to tell the server: "Hey, don't send me a WWW-Authenticate, send a X-WWW-Authenticate instead so I can deal with it".

By the way, even if the information is easy to find, this is how the digest authentication is done:

  • Client makes request to http://webserver.com/url1/index.html
  • Server sends a "WWW-Authenticate: realm="testrealm", nonce="testnonce"
  • ha1 = md5("username:testrealm:password")
  • ha2 = md5("GET:/url1/index.html")
  • ha3 = md5(ha1+":testnonce:"+ha2)
  • Client sends: "Authorization: Digest username="username", realm="testrealm", nonce="testnonce", response=""+ha3+"", uri="/url1/index.html"