Web Database Applications with PHP \& MySQLWeb Database Applications with PHP \& MySQLSearch this book

9.2. HTTP Authentication with PHP

PHP can access the credentials collected using the HTTP mechanisms introduced in the last section, and can actually manage the HTTP authentication without relying on Apache's configuration.

9.2.1. Access to User Credentials from PHP

PHP provides access to the encoded credentials from the HTTP Authorized header field through the global variables $PHP_AUTH_USER, $PHP_AUTH_PW, and $PHP_AUTH_TYPE. PHP initializes the variable $PHP_AUTH_USER with the username and $PHP_AUTH_PW with the password entered into the browser authentication dialog box. The global variable $PHP_AUTH_TYPE is initialized with the encoding type used by the browser; typically this value is set to Basic.

The script shown in Example 9-3 reads the authentication global variables and displays them in the body of the response. For the PHP code in Example 9-3 to display the authentication credentials, the script needs to be requested after a user has been challenged for a username and password. This happens if the file containing the script is placed within a directory configured by Apache to require authentication.

Example 9-3. PHP access to authentication

<!DOCTYPE HTML PUBLIC 
    "-//W3C//DTD HTML 4.0 Transitional//EN"
    "http://www.w3.org/TR/html4/loose.dtd" >
<html>
  <head><title>Authentication</title></head>
  <body>
    <h2>Hi there <?=$PHP_AUTH_USER?></h2>

    <p>Thank you for your password
                 '<?=$PHP_AUTH_PW?>'!

  </body>
</html>

Applications can use the encoded credentials to support features that rely on identifying the user. For example, an application that charges on a per-page view basis might use the $PHP_AUTH_USER variable when recording an access to a particular page. In this way, Apache can provide the authentication, and the application records the users' usage. While this approach removes the need to write any PHP code to implement authentication, users and passwords need to be maintained in an Apache password file. In the next section we describe how to manage HTTP authentication from within a PHP script, thus relieving Apache of authentication responsibilities and allowing different logic to be applied to the authorization of requests.

9.2.2. Managing HTTP Authentication with PHP

Rather than configuring Apache to authenticate requests, PHP scripts can manage the HTTP authentication challenge directly. Scripts can be written to test the $PHP_AUTH_USER and $PHP_AUTH_PW variables and send a response containing the WWW-Authenticate header to challenge the browser. When a request contains a username and password, the script can authenticate and authorize the request using any logic that is required. In Example 9-4 the user credentials set in the $PHP_AUTH_USER and $PHP_AUTH_PW variables are passed to the function authenticated( ). This function uses the unsophisticated authentication scheme of checking that the password is the same as the username. In the next section we show how to implement a secure scheme that stores passwords in a database.

Example 9-4. Script generates an unauthorized response if credentials aren't in request

<?php
function authenticated($username, $password)
{
  // If either the username or the password are
  // not set, the user is not authenticated
  if (!isset($username) || !isset($password))
    return false;

  // If the username is the same as the password
  // then the user is authenticated
  if ($username == $password)
    return true;
  else
    return false;
}

//Main --------

if(!authenticated($PHP_AUTH_USER, $PHP_AUTH_PW))
{
  // No credentials found - send an unauthorized
  // challenge response
  header("WWW-Authenticate: Basic realm=\"Flat Foot\"");
  header("HTTP/1.0 401 Unauthorized");

  // Set up the body of the response that is 
  // displayed if the user cancels the challenge
  ?>
  <!DOCTYPE HTML PUBLIC 
    "-//W3C//DTD HTML 4.0 Transitional//EN"
    "http://www.w3.org/TR/html4/loose.dtd" >
   <html>
     <head>
       <title>Web Database Applications</title>
     </head>
     <body>
       <h2>You need a username and password to 
               access this service</h2>
       <p>If you have lost or forgotten your 
               password, tough!
     </body>
  </html>
  <?php

  exit;
}

// The response to authorized users
?>
<!DOCTYPE HTML PUBLIC 
   "-//W3C//DTD HTML 4.0 Transitional//EN"
   "http://www.w3.org/TR/html4/loose.dtd" >
<html>
  <head>
    <title>Web Database Applications</title>
  </head>
  <body>
    <h2>Welcome!</h2>
  </body>
</html>

The authenticated( ) function returns false if either the $username or $password hasn't been set, or if the two values aren't the same. If the user credentials fail the test, you respond with the header field WWW-Authenticate with the encoding scheme Basic and the realm name Flat Foot. You can also set the response line to include the status code 401 Unauthorized. The PHP manual suggests sending the WWW-Authenticate header before the HTTP/1.0 401 Unauthorized header to avoid problems with some versions of Internet Explorer browsers.

The first time a browser requests this page, the script sends a challenge response containing the 401 Unauthorized header field. If the user cancels the authentication challenge, usually by clicking the cancel button in a dialog box that collects the credentials, the HTML encoded in the challenge response is displayed.

While the script shown in Example 9-4 duplicates much of the HTML used for the authorized response and the challenge response, you can't simplify the script by putting the common HTML at the start of the file. Because the script calls the header( ) function when credentials aren't included in the request or the supplied credentials don't authenticate, you can't output any of the response body until you know if the user has authenticated.

9.2.3. Authorizing User Access

Writing PHP scripts to manage the authentication process allows for flexible authorization logic to be applied when processing a request. Authenticating a user successfully against a list or table of known users doesn't automatically authorize that user to access an application. For example, an application might apply restrictions based on group membership: a user belonging to the DIRECTORS group gets to see the reports from the budget database, while others can't. The number of schemes for restricting access is limited only by a developer's imagination or more often by that of the marketing department. A user of a subscription-based service might supply a correct username and password, but be denied access when a fee is 14 days overdue. Access might be denied on Thursday evenings when system maintenance is performed. Implementing such authorization schemes requires designing the appropriate user table or tables.

There are several HTTP status codes that are appropriate to use when denying access to a user. Earlier, we used the response code of 401 Unauthorized to control HTTP authentication. The response status code of 403 Forbidden is appropriate if an explanation as to why access has been denied is required. Example 9-5 uses the code of 403 Forbidden. The HTTP/1.1 standard describes 17 4xx status codes that have various meanings. The infamous 404 Not Found is returned by Apache if the requested resource doesn't exist, and a PHP script can return this code if the exact reason for the refusal needs to be hidden. The code 402 Payment Required has been included, but the HTTP standard has not provided an interpretation of how it should be used.

9.2.3.1. Limits placed on IP addresses

A PHP script can access the IP address from which a request was sent by inspecting the server variable $REMOTE_ADDR. This remote address can restrict access. A simple example allows access only from a specific IP address. This can be used to implement administration scripts that allow access only from a specific computer. A variation, shown in Example 9-5, is to allow access to users on a particular network subnet. Example 9-5 limits access to the main content of the script to requests sent from clients with a range of IP addresses that begin with 141.190.17.

Example 9-5. PHP script that forbids access from browsers outside an IP subnet

<?php
if(strncmp("141.190.17", $REMOTE_ADDR, 10) != 0)
{
    header("HTTP/1.0 403 Forbidden");
  ?>
  <!DOCTYPE HTML PUBLIC 
      "-//W3C//DTD HTML 4.0 Transitional//EN"
      "http://www.w3.org/TR/html4/loose.dtd" >
  <html>
    <head><title>Marketing Department</title></head>
    <body>
      <h2>403 Forbidden</h2>
      <p>You cannot access this page from outside 
         the Marketing Department.
    </body>
  </html>
  <?
  exit;
}
?>
<!DOCTYPE HTML PUBLIC 
    "-//W3C//DTD HTML 4.0 Transitional//EN"
    "http://www.w3.org/TR/html4/loose.dtd" >
<html>
  <head><title>Marketing Department</title></head>
  <body>
    <h2>Marketing secrets!</h2>
    <p>Need new development team - the old one
      says <em>No</em> far too often.
  </body>
</html>

Another limit that can be applied using the IP address is to help prevent session hijacking—a problem discussed later in this chapter.



Library Navigation Links

Copyright © 2003 O'Reilly & Associates. All rights reserved.