8.10.1 Problem
You want to use
PHP to protect parts of your web site with passwords.
Instead of storing the passwords in an external file and letting the web server
handle the authentication, you want the password verification logic to be in a
PHP program.
8.10.2 Solution
The $_SERVER['PHP_AUTH_USER']
and
$_SERVER['PHP_AUTH_PW'] global variables contain the username and
password supplied by the user, if any. To deny access to a page, send a
WWW-Authenticate header identifying the authentication realm as part of
a response with status code 401:
header('WWW-Authenticate: Basic realm="My Website"');
header('HTTP/1.0 401 Unauthorized');
echo "You need to enter a valid username and password.";
exit;
8.10.3 Discussion
When a browser sees a 401 header, it pops up a dialog box for a
username and password. Those authentication credentials (the username and
password), if accepted by the server, are associated with the realm in the
WWW-Authenticate header.
Code that checks authentication credentials needs to be executed before any
output is sent to the browser, since it might send headers. For example, you can
use a function such as pc_validate( ), shown in
Example 8-2.
Example 8-2. pc_validate( )
function pc_validate($user,$pass) {
/* replace with appropriate username and password checking,
such as checking a database */
$users = array('david' => 'fadj&32',
'adam' => '8HEj838');
if (isset($users[$user]) && ($users[$user] == $pass)) {
return true;
} else {
return false;
}
}
Here's an example of how to use pc_validate():
if (! pc_validate($_SERVER['PHP_AUTH_USER'], $_SERVER['PHP_AUTH_PW'])) {
header('WWW-Authenticate: Basic realm="My Website"');
header('HTTP/1.0 401 Unauthorized');
echo "You need to enter a valid username and password.";
exit;
}
Replace the contents of the pc_validate( ) function
with appropriate logic to determine if a user entered the correct password. You
can also change the realm string from "My Website" and the message that gets
printed if a user hits "cancel" in their browser's authentication box from "You
need to enter a valid username and password."
HTTP Basic authentication can't be used if you're running PHP
as a CGI. If you can't run PHP as
a server module, you can use cookie authentication, discussed in Section 8.11.
Another issue with HTTP Basic
authentication is that it provides no simple way for a user to log out, other
then to exit his browser. The PHP online manual has a few suggestions for log
out methods that work with varying degrees of success with different server and
browser combinations at http://www.php.net/features.http-auth.
There is a
straightforward way, however, to force a user to log out after a fixed time
interval: include a time calculation in the realm string. Browsers use the same
username and password combination every time they're asked for credentials in
the same realm. By changing the realm name, the browser is forced to ask the
user for new credentials. For example, this forces a log out every night at
midnight:
if (! pc_validate($_SERVER['PHP_AUTH_USER'],$_SERVER['PHP_AUTH_PW'])) {
$realm = 'My Website for '.date('Y-m-d');
header('WWW-Authenticate: Basic realm="'.$realm.'"');
header('HTTP/1.0 401 Unauthorized');
echo "You need to enter a valid username and password.";
exit;
}
You can also have a user-specific timeout without changing the
realm name by storing the time that a user logs in or accesses a protected page.
The pc_validate() function in Example 8-3 stores login time in a database and
forces a log out if it's been more than 15 minutes since the user last requested
a protected page.
Example 8-3. pc_validate2( )
function pc_validate2($user,$pass) {
$safe_user = strtr(addslashes($user),array('_' => '\_', '%' => '\%'));
$r = mysql_query("SELECT password,last_access
FROM users WHERE user LIKE '$safe_user'");
if (mysql_numrows($r) == 1) {
$ob = mysql_fetch_object($r);
if ($ob->password == $pass) {
$now = time();
if (($now - $ob->last_access) > (15 * 60)) {
return false;
} else {
// update the last access time
mysql_query("UPDATE users SET last_access = NOW()
WHERE user LIKE '$safe_user'");
return true;
}
}
} else {
return false;
}
}
For example:
if (! pc_validate($_SERVER['PHP_AUTH_USER'],$_SERVER['PHP_AUTH_PW'])) {
header('WWW-Authenticate: Basic realm="My Website"');
header('HTTP/1.0 401 Unauthorized');
echo "You need to enter a valid username and password.";
exit;
}