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.
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.
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.
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.
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.
18.190.156.93