• Home
  • About Me
  • Contact me
  • favorite words
  • My Guest Book
  • People I’d like to thank
  • Photos
Blue Orange Green Pink Purple

HTTP Basic and Digest authentication with PHP

Posted in AJAX, Contributors, Google, Graphics, Javascript, MVC, PEAR, PHP Help, PHP Jobs, PHP News, PHP Technologies, PHP Tips, Uncategorized, Wordpress, ZEND. on Thursday, February 12th, 2009 by Mahmoud M. Abdel-Fattah
Feb 12

HTTP authentication is quite popular for web applications. It is pretty
easy to implement and works for a range of http applications; not to mention
your browser.

Basic Auth

The two main authentication schemes are ‘basic’ and ‘digest’. Basic is pretty
easy to implement and appears to be the most common:


<?php

$username = null;

$password = null;

// mod_php

if (isset($_SERVER['PHP_AUTH_USER'])) {

$username = $_SERVER['PHP_AUTH_USER'];

$password = $_SERVER['PHP_AUTH_PW'];

// most other servers

} elseif (isset($_SERVER['HTTP_AUTHENTICATION'])) {

if (strpos(strtolower($_SERVER['HTTP_AUTHENTICATION']),‘basic’))

list($username,$password) = explode(‘:’,base64_decode(substr($_SERVER['HTTP_AUTHORIZATION'], 6)));

}

if (is_null($username)) {

header(‘WWW-Authenticate: Basic realm=”My Realm”‘);

header(‘HTTP/1.0 401 Unauthorized’);

echo ‘Text to send if user hits Cancel button’;

die();

} else {

echo “<p>Hello {$username}.</p>”;

echo “<p>You entered {$password} as your password.</p>”;

}

?>
Well it’s a bit difficult I suppose, but you might have noticed the username
and password are sent over the wire using base64 encoding. Not really secure, unless
you have SSL in place.

Digest

Digest is designed to be more secure. The password is never sent over the wire
in plain text, but rather as a hash. The implications of the usage of a hash is that
it can never be decrypted. We can only validate the hash by applying the same hash function
to the password we have. If the hashes match, the password was correct.

Lets first see how Digest auth should work:

Client requests url


GET / HTTP/1.1

Server requires authentication


HTTP/1.1 401 Unauthorized

WWW-Authenticate: Digest realm="The batcave",

qop="auth",

nonce="4993927ba6279",

opaque="d8ea7aa61a1693024c4cc3a516f49b3c"


Client authenticates


GET / HTTP/1.1

Authorization: Digest username="admin",

realm="The batcave",

nonce=49938e61ccaa4,

uri="/",

response="98ccab4542f284c00a79b5957baaff23",

opaque="d8ea7aa61a1693024c4cc3a516f49b3c",

qop=auth, nc=00000001,

cnonce="8d1b34edb475994b"

Information coming from the server:

realm A string which will be used within the UI and as part of the hash.
qop Can be auth and auth-int and has influence on how the hash is created. We use auth.
nonce A unique code, which will be used within the hash and needs to be sent back by the client.
opaque This can be treated as a session id. If this changes the browser will deauthenticate the user.

Information from the client:

username The supplied username
realm Same as server response.
nonce Same as server response.
uri The authentication uri
response The validation hash.
opaque Same as server response.
qop Same as server response.
nc Nonce-count. This a hexadecimal serial number for the request. The client should increase this number by one for every request.
cnonce A unique id generated by the client

So how do we know if the password was correct? We van validate using the following formula (pseudo code).


A1 = md5(username:realm:password)

A2 = md5(request-method:uri) // request method = GET, POST, etc.

Hash = md5(A1:nonce:nc:cnonce:qop:A2)

if (Hash == response)

//success!

else

//failure!

Or, using PHP:


<?php

$realm = ‘The batcave’;

// Just a random id

$nonce = uniqid();

// Get the digest from the http header

$digest = getDigest();

// If there was no digest, show login

if (is_null($digest)) requireLogin($realm,$nonce);

$digestParts = digestParse($digest);

$validUser = ‘admin’;

$validPass = ’1234′;

// Based on all the info we gathered we can figure out what the response should be

$A1 = md5(“{$digestParts['username']}:{$realm}:{$validPass}”);

$A2 = md5(“{$_SERVER['REQUEST_METHOD']}:{$digestParts['uri']}”);

$validResponse = md5(“{$A1}:{$digestParts['nonce']}:{$digestParts['nc']}:{$digestParts['cnonce']}:{$digestParts['qop']}:{$A2}”);

if ($digestParts['response']!=$validResponse) requireLogin($realm,$nonce);

// We’re in!

echo ‘Well done sir, you made it all the way through the login!’;

// This function returns the digest string

function getDigest() {

// mod_php

if (isset($_SERVER['PHP_AUTH_DIGEST'])) {

$digest = $_SERVER['PHP_AUTH_DIGEST'];

// most other servers

} elseif (isset($_SERVER['HTTP_AUTHENTICATION'])) {

if (strpos(strtolower($_SERVER['HTTP_AUTHENTICATION']),‘digest’))

$digest = substr($_SERVER['HTTP_AUTHORIZATION'], 7);

}

return $digest;

}

// This function forces a login prompt

function requireLogin($realm,$nonce) {

header(‘WWW-Authenticate: Digest realm=”‘ . $realm . ‘”,qop=”auth”,nonce=”‘ . $nonce . ‘”,opaque=”‘ . md5($realm) . ‘”‘);

header(‘HTTP/1.0 401 Unauthorized’);

echo ‘Text to send if user hits Cancel button’;

die();

}

// This function extracts the separate values from the digest string

function digestParse($digest) {

// protect against missing data

$needed_parts = array(‘nonce’=>1, ‘nc’=>1, ‘cnonce’=>1, ‘qop’=>1, ‘username’=>1, ‘uri’=>1, ‘response’=>1);

$data = array();

preg_match_all(‘@(\w+)=(?:(?:”)([^"]+)”|([^\s,$]+))@’, $digest, $matches, PREG_SET_ORDER);

foreach ($matches as $m) {

$data[$m[1]] = $m[2] ? $m[2] : $m[3];

unset($needed_parts[$m[1]]);

}

return $needed_parts ? false : $data;

}

?>
As you can see we need to have a plain-text version of the password in order to
validate the user. It’s not a good idea to store the plain-text password, therefore
it’s strongly recommended to store the result of $A1 instead.

Security improvements

  • It’s smart to validate the contents of opaque, nonce and realm. If you have
    the data stored on the server, why not check it.
  • The nc should be an ever increasing number. You could store the number and
    track to make sure it doesn’t make any big jumps. It’s not wanted to be extremely
    strict about the sequence, because you might miss a number, and requests could come in
    be out of order.
  • ‘qop’ is quality of protection. This serves as an integrity code for the request.
    A hacker could steal all your HTTP Digest headers and simply change the body to make it do
    something else. If ‘qop’ is set to ‘auth’, only the requested uri will be taken
    into consideration. If ‘qop’ is ‘auth-int’ the body of the request will also be used in the hash. (A2 = md5(request-method:uri:md5(request-body))).

References:

  • RFC 2617.
  • PHP.net.
  • Thomas Pike’s blog.

Leave a Reply

Anti-Spam Protection by WP-SpamFree

Mahmoud M. Abdel-Fattah

Hdeya.com
Hdeya.com for IT Solutions
  • Arabic Aviva Beginners Blackberry Branding Business Coding Development Directory Egyptian el esseily Etisalat Free Global Global Knowledge Partnership Supporting young social entrepreneurs green Green line Grow Knowledge line mahmoud Numbers Online origin Partnership Pharaos PHP Pyramids of Giza rassam social Standards Start-up Supporting Tests Themes video clip walad walad rassam Web 2.0 where it come from Wordpress young Youth الشباب المصرى

    WP Cumulus Flash tag cloud by Roy Tanck and Luke Morton requires Flash Player 9 or better.

  • Random Photos
    it was cold !
  • Recent Posts
    • CEO Vs. CEO
    • 3abdel-fatta7 3al BBC ya menzz
    • Startup and change the world , Recommended book.
    • Plastic Nescafe Classic
    • What if pharaohs had a Blackberry™ when they were building the Great Pyramids !
  • Recent Comments
    • Mahmoud M. Abdel-Fattah on CEO Vs. CEO
    • she7ata on CEO Vs. CEO
    • Mayada on CEO Vs. CEO
    • Osama Al Kersh on CEO Vs. CEO
    • Diaa on CEO Vs. CEO
  • Archives
    • July 2010
    • October 2009
    • September 2009
    • July 2009
    • June 2009
    • May 2009
    • April 2009
    • March 2009
    • February 2009
    • January 2009
    • December 2008
    • October 2008
    • September 2008
    • August 2008
    • December 2007
    • November 2007
    • December 2006
    • November 2006
    • October 2006
  • Search






  • Home
  • About Me
  • Contact me
  • favorite words
  • My Guest Book
  • People I’d like to thank
  • Photos

© Copyright Mahmoud M. Abdel-Fattah. All rights reserved.
Designed by FTL Wordpress Themes brought to you by Smashing Magazine

Back to Top