Core Web Application Development with PHP and MySQL

Most major web servers (including the Apache HTTP Server and the Microsoft IIS server) and PHP provide basic authentication mechanisms. These mechanisms are easy to use and do not require very much code in the web application. However, they have a number of drawbacks that are mentioned in the following sections.

Basic HTTP Authentication

The most straightforward authentication scheme is HTTP Authentication. You can access this within PHP by using the header function, which we have seen in previous chapters (such as Chapter 7, "Interacting with the Server: Forms"). Most of this mechanism's work is done via the HTTP protocol, which leaves your web application only to verify whether the credentials provided by users are accepted. If so, they can have access to any pages that you protect with this scheme.

How It Works

The details of this process are not complicated. The client requests a page, which causes an HTTP GET request to be sent to the server:

[View full width]

GET /super_secret.php HTTP/1.1 Host: phpsrvr User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; rv:1.7.3) Gecko/20040913 Firefox/0.10.1 Accept: text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8 ,image/png,*/*;q=0.5 Accept-Language: en-us,en;q=0.5 Accept-Encoding: gzip,deflate Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7 Keep-Alive: 300 Connection: keep-alive

The server, recognizing that this is a page that only members can access, sends a special response to indicate that authorization is required instead of the usual response with content:

HTTP/1.x 401 Authorization Required Date: Thu, 13 Jan 2005 01:51:31 GMT Server: Apache/1.3.33 (Unix) PHP/5.0.3 X-Powered-By: PHP/5.0.3 WWW-Authenticate: Basic realm="Web Application Members Area" Keep-Alive: timeout=15, max=98 Connection: Keep-Alive Transfer-Encoding: chunked Content-Type: text/html

Upon receiving this response, the client browser puts up a dialog box asking the user to provide authentication credentials for the server; the Microsoft Internet Explorer version of this is shown in Figure 20-4.

Figure 20-4. The HTTP Authentication dialog box.

After the user provides the set of credentials, the client browser resends the request to the server. This time, there is a new field in the HTTP GET header:

Authorization: Basic Y2hpcHB5X3RoZV9jaGlwbXVuazpjaGVzdG51dHM=

This header indicates that authentication information for Basic HTTP Authentication is being sent with the request. The garbled text after the keyword Basic is base64 encoded data (a mechanism for taking arbitrary binary data and converting the bytes into a stream of one of 64 different ASCII characters, with trailing (=) characters if necessary).

The server then disassembles the authentication data, validates it, and sends the contents of the page with a normal response if it is accepted:

HTTP/1.x 200 OK Date: Thu, 13 Jan 2005 02:08:54 GMT Server: Apache/1.3.33 (Unix) PHP/5.0.3 X-Powered-By: PHP/5.0.3 Keep-Alive: timeout=15, max=100 Connection: Keep-Alive Transfer-Encoding: chunked Content-Type: text/html etc...

Our client browser, knowing that the authentication credentials work, proceeds to resend them with every subsequent request to the server until every browser window is closed. The user is not asked for authentication information again.

There are two problems with this mechanism. First, the mechanism works with client browser-provided dialog boxes, resulting in suboptimal integration of the login process in to your web application.

Second, the username and password are sent over the network in an unencrypted format. You may look at the preceding base64 data and think that it looks encrypted, but a simple command reveals the previous stream to be

chippy_the_chipmunk:chestnuts

Base64 encoding merely takes arbitrary data (which may be printable text) and converts it into something that is printable. It is a trivially reversible process, and users on Unix or Mac OS X systems can decode base64 text by executing the following code:

echo [base64text] | openssl base64 d

Since our client browser is sending the authentication data with every request that goes to the server, we should definitely be nervous about the safety of our passwords. If we use this mechanism, we should make sure that users are using passwords they do not use for other critical applications, such as their bank accounts or automated bank teller (ATM) PIN numbers. We can mitigate some of the risk by using an SSL connection for the portions of the web site that use this style of authentication, but we should still be very careful. Users cannot log out of our system without closing all of their browser windows.

However, for casual member authentication, this is an extremely quick and easy-to-implement mechanism for validating users.

Implementing Basic HTTP Authentication

You can use Basic HTTP Authentication in PHP if your web server is the Apache httpd server, or if the Microsoft IIS is running PHP as an ISAPI module. This scheme does not work for either server if PHP runs as a CGI module. (See Appendix A, "Installation/Configuration," for more detail.) Unfortunately, the servers have different code that needs to be sent and different ways in which the resulting authentication data is presented to the PHP script.

When the server receives the Authorization: Basic header in a request, it takes the data and puts it in one of two places:

  • PHP on Apache puts the username in $_SERVER['PHP_AUTH_USER'] and the password in $_SERVER['PHP_AUTH_PASSWORD'].

  • PHP operating in Microsoft's IIS places both the username and password, separated by a colon character (and still in base64 format), in $_SERVER ['HTTP_AUTHORIZATION'].

If we see, in our script, that we do not have the variables or the authentication information for the user in the script, then we need to send the headers back to the client and indicate that we need this information to continue. We put this code in a function called send_auth_headers, as follows:

<?php // // look for the string 'Microsoft' in the server software // identification to see if we're running IIS // $msft_srvr = (substr($_SERVER['SERVER_SOFTWARE'], 0, 9) == "Microsoft") ? TRUE : FALSE; function send_auth_headers() { // nb. there can be NO output before the header fn! header('WWW-Authenticate: Basic realm="Members Area"'); if ($msft_srvr) { header('Status: 401 Unauthorized'); } else { header('HTTP/1.0 401 Unauthorized'); } // // if the user authentication fails, then the following is // what gets displayed to him. // echo <<<EOM <p align='center'> You must provide a valid login name and password before proceeding into this portion of the system. </p> EOM; exit; } ?>

Both servers require the first header, which indicates the name of the area to which access is being attempted, or realm (which was previously called "Members Area"). This realm and its name are defined by you. Unfortunately, the second header differs between the two servers. Microsoft's IIS requires the HTTP response code of 401 to be preceded by the Status: keyword, while Apache httpd requires HTTP/1.0.

When authentication fails, the two headers are presented to the user in the client browser, as shown in the code snippet that followed. This text is brief and is only used for demonstration purposes; ideally, you would provide something more helpful in your applications.

You must now look at the code to perform the user authentication:

<?php // // look for the string 'Microsoft' in the server software // identification to see if we're running IIS // $msft_srvr = (substr($_SERVER['SERVER_SOFTWARE'], 0, 9) == "Microsoft") ? TRUE : FALSE; // handle the IIS special case first. if ($msft_srvr && !isset($_SERVER['PHP_AUTH_USER']) && !isset($_SERVER['PHP_AUTH_PW']) && substr($_SERVER['HTTP_AUTHORIZATION'], 0, 6) == 'Basic')) { // strip off the "Basic" $http_auth = substr($_SERVER['HTTP_AUTHORIZATION'], 6); // decode -- IIS gives us the data in base64 !!! $http_auth = base64_decode($http_auth); // split it up $parts = split($http_auth, ':'); $BASIC_AUTH_USERNAME = $parts[0]; $BASIC_AUTH_PASSWORD = $parts[1]; } if (!isset($BASIC_AUTH_USERNAME)) { $BASIC_AUTH_USERNAME = isset($_SERVER['PHP_AUTH_USER']) ? $_SERVER['PHP_AUTH_USER'] : ''; $BASIC_AUTH_PASSWORD = isset($_SERVER['PHP_AUTH_PW']) ? $_SERVER['PHP_AUTH_PW'] : ''; } // send the auth headers if we still don't have user creds. // validate_user is some function you can write to verify // a username/password if ($BASIC_AUTH_USERNAME == '' or !validate_user($BASIC_AUTH_USERNAME, $BASIC_AUTH_PASSWORD)) { send_auth_headers(); } echo <<<EOM <p align='center'> Thank you for logging in {$_SERVER['PHP_AUTH_USER']}. Your super secret-password, which we are now transmitting as plain text over the Internet is: {$_SERVER['PHP_AUTH_PW']} </p> EOM; ?>

The following is done in the preceding code:

  1. We try to determine the user authentication credentials.

  2. If we cannot determine them, we send the headers and exit.

  3. If we determine that the credentials are invalid, we send the headers and exit.

  4. Otherwise, we continue to process the page.

The nice thing about all of this code is that we can easily put it into an include file and just include the sections we want to protect at the top of our script files:

<?php /* show_member_list.php */ // this takes care of authentication require_once('../include/http_auth.inc'); // and so on ... ?>

Microsoft Windows Authentication Schemes

Another type of authentication in Microsoft's IIS is provided by the Microsoft Windows operating system. IIS can require users to be authenticated by the same system they use to log in to their Windows machines. Although this is not useful over the open Internet, it is useful for intranets that are largely or entirely based on Windows domains and their authentication mechanisms.

There are a few distinct advantages to this authentication mechanism. First, it is entirely administered by the server, which saves us from writing code. We merely have to configure IIS to protect a file, set of files, or directory tree, and access is only granted to the desired users. Second, it does not transmit passwords in an easily decoded format. The authentication mechanisms used by the Microsoft server are more robust and secure. Finally, the set of users is managed by the server and its associated Windows authentication mechanisms, which means that we do not have to worry about keeping files or tables up-to-date with usernames and passwords.

Configuration

Configuration of this Microsoft functionality is done through a series of dialog boxes. Only the procedure on IIS 5.1 is demonstrated here, but it is similar for most versions. To begin the configuration, you bring up the Internet Information Services Management Console (found in C:\windows\system32\inetsrv\iis.msc) and select the default web site, as shown in Figure 20-5.

Figure 20-5. The Internet Information Services Management Console.

You then right-click any directory (or directories) that you would like to protect and bring up the Properties dialog box for it. After this, you select the Directory Security tab.

Next, click the "Edit" button under Anonymous Access and Authentication Control, which brings up another dialog box called Authentication Methods. This dialog box has three important check boxes along the left edge:

  • Anonymous Access Uncheck this box to disable it. We want all visitors to our page(s) to be authenticated, so we disable anonymous access.

  • Basic Authentication We will not use basic authentication on these pages, so uncheck this.

  • Integrated Windows Authentication This has IIS use, as we have mentioned, the Windows login and authentication system. We check this one to indicate that we want to use Windows authentication.

A picture of the dialog box and how we have configured it is shown in Figure 20-6.

Figure 20-6. The Authentication Methods dialog box for Microsoft IIS.

Категории