Page 1 of 1

HTTP Digest authentication with encrypted passwords

Posted: Wed Apr 15, 2009 6:28 am
by marxJ
Hi everyone, I'm currently trying to implement HTTP Digest authentication to a script. I've had a thorough read of the documentation as well as a few examples, but all of then have the same problem in that they rely on the passwords being stored as plain text, a practice I've never been keen on.

The way I've tried to get around it is instead of storing a hash of the password, storing the a1 hash in the database (i.e. md5($username.":".$realm.":".$password); ). I've got this working OK for authentication through a form, but not through HTTP. Here's the class I've written:

Code: Select all

class auth_digest {
    
    public $username;
    public $nonce;
    public $uri;
    public $response;
    public $qop;
    public $nc;
    public $cnonce;
    
    public function __construct($digest) {
        if(preg_match('/username="([^"]+)"/', $digest, $username)
        && preg_match('/nonce="([^"]+)"/', $digest, $nonce)
        && preg_match('/uri="([^"]+)"/', $digest, $uri)
        && preg_match('/response="([^"]+)"/', $digest, $response)
        && preg_match('/qop="?([^,\s"]+)/', $digest, $qop)
        && preg_match('/nc=([^,\s"]+)/', $digest, $nc)
        && preg_match('/cnonce="([^"]+)"/', $digest, $cnonce)) {
            $this->username = $username[1];
            $this->nonce = $nonce[1];
            $this->uri = $uri[1];
            $this->response = $response[1];
            $this->qop = $qop[1];
            $this->nc = $nc[1];
            $this->cnonce = $nonce[1];
            return true;
        } else {
            return false;
        }
    }
    
    public function authenticate($hash) {
        $A1 = $hash;
        $A2 = md5($_SERVER['REQUEST_METHOD'].':'.$this->uri);
        $valid_response = md5($A1.':'.$this->nonce.':'.$this->nc.':'.$this->cnonce.':'.$this->qop.':'.$A2);
        if ($valid_response ==  $this->response) {
            return true;
        } else {
            return false;
        }
    }
};
The idea is that you pass $_SERVER['PHP_AUTH_DIGEST'] to the constructor which pulls out all the relevant data and stores it in the properties. This seems to work fine. You then get the user's hash from the database (using the username property in the query) and pass it into the authenticate() method, which hashes it and compares it to the digest sent with the request. However, for some reason, the digest generated by the script isn't matching that sent with the request. Can anyone see something I'm doing wrong?