ÄúµÄλÖãºÑ°ÃÎÍøÊ×Ò³£¾±à³ÌÀÖÔ°£¾PHP ±à³Ì£¾PHP 5 Power programming
Team LiB
Previous Section Next Section

11.4. Authentication

PEAR Auth is an abstracted authentication layer, with "containers" for interfacing with various authentication systems. It supports regular password files, databases accessed through DB or MDB, as well as IMAP, POP3, LDAP, RADIUS, SOAP, and Samba (Windows domain) logons.

11.4.1. Overview

The Auth package uses a POST request for passing usernames and passwords. The username and password are checked in a container object that implements the interface with the authentication back-end (such as a password file, a MySQL database, or an LDAP server). When login succeeds, Auth uses sessions to keep track of the user. In practice, the PHP session works as an authentication ticket, which is a single piece of information that gives login access for a limited amount of time.

Using sessions to track the user has the advantage that the authentication check, which may be expensive for some back-ends, is done once for the session rather than once per HTTP request. The Auth package also provides mechanisms for expiring a session after a set time from login, or after a set idle time.

Your application may store addition data along with the Auth data; you will see an example of this later.

11.4.2. Example: Auth with Password File

The following example shows typical Auth usage using the file container. The file container requires that you have the File_Passwd package installed.

<?php

require_once 'Auth.php';

$auth = new Auth("File", ".htpasswd", "login_function");
$auth->start();
if (!$auth->getAuth()) {
    exit;
}

if (!empty($_REQUEST['logout'])) {
    $auth->logout();
    print "<h1>Logged out</h1>\n";
    print "<a href=\"$_SERVER[PHP_SELF]\">Log in again</a>\n";
    exit;
}

print "<h1>Logged in!</h1>\n";

if (!empty($_REQUEST['dump'])) {
    print "<pre>SESSION=";
    var_dump($_SESSION);
    print "</pre>\n";
} else {
    print "<a href=\"$_SERVER[PHP_SELF]?dump=1\">Dump session</a><br>\n";
}

print "<a href=\"$_SERVER[PHP_SELF]?logout=1\">Log Out</a>\n";

// --- execution ends here ---

function login_function()
{
    print "<h1>Please Log In</h1>\n";
    print "<form action=\"$_SERVER[PHP_SELF]\" method=\"POST\">\n";
    print "User name: <input name=\"username\"> ";
    print "Password: <input name=\"password\"> ";
    print "<input type=\"submit\" value=\"Log In\">\n";
    print "</form>\n";
    exit;
}

The example password file (the username is "guest," and the password is blank) is

guest:Z3kgRZpxQPbjo

This example script starts by creating an Auth object using .htpasswd as a password file.

The $auth->start() call sets up the PHP session (you do not need to run session_start() in advance), reads the POST variables, checks the submitted username and password, and calls login_function() if the login failed.

This example script first displays a login form. After you log in as a guest (with no password), you should get two links: Dump session and Log Out.

11.4.3. Example: Auth with DB and User Data

This example adds on to the previous example by using a database for username and password, and does not provide a custom login form. Instead, a built-in login form is used.

In addition, you learn how to attach additional user-related information to the login session, and how to implement auto-expiring login sessions. To give you a better idea of how the login information is stored in the session, here is an example of Auth session data:

$_SESSION["_authsession"] = array(
    "data" => array(),
    "registered" => 1,
    "username" => "guest"
    "timestamp" => 1075642673,
    "idle" => 1075643017,
)

The PHP session variable that holds the Auth session is always called _authsession. The keys within this array are shown in Table 11.5.

Table 11.5. Auth Session Variables

Key Name

Description

data

This is where the user-provided Auth session data is stored. This could be set directly with setAuthData(), or loaded from the database when the db_field option to Auth_Container_DB is specified.

registered

Always set to TRUE when the user is logged in.

username

Holds the username.

timestamp

Contains time() when the user logged in.

idle

Contains time() of last session activity.


Note

The password is not stored in the session. It does not have to bethe user is already authenticated. The session only contains information that was retrieved upon successful authentication, and some that is updated constantly after authentication (such as idle and, optionally, data).


This session array is just part of what goes behind the scenes; you never need to deal with it directly.

Seeing it is useful to better understand how the Auth works. For example, to expire the user's login after N hours, Auth checks the timestamp session variable. In addition, to expire the user's login after N minutes of inactivity, Auth checks the idle session variable.

Here is the code:

<?php

require_once 'DB.php';
require_once 'PEAR.php';
require_once 'Auth.php';
require_once 'HTML/QuickForm.php';

$auth_options = array(
    'dsn' => 'mysql://test@localhost/test',
    'table' => 'users',
    'usernamecol' => 'username',
    'passwordcol' => 'password',
    'db_fields' => '*',
    );
PEAR::setErrorHandling(PEAR_ERROR_DIE);
$auth = new Auth('DB', $auth_options, 'login_function');

$auth->start();
if (!$auth->getAuth()) {
    exit;
}

if (!empty($_REQUEST['logout'])) {
    $auth->logout();
    print "<h1>Logged out</h1>\n";
    print "<a href=\"$_SERVER[PHP_SELF]\">Log in again</a>\n";
    exit;
}

print "<h1>Logged in!</h1>\n";

if (!empty($_REQUEST['dump'])) {
    print "<pre>_authsession = ";
    print_r($_SESSION['_authsession']);
    print "</pre>\n";
} else {
    print "<a href=\"$_SERVER[PHP_SELF]?dump=1\">Dump session</a><br>\n";
}

print "<a href=\"$_SERVER[PHP_SELF]?logout=1\">Log Out</a>\n";

// --- execution ends here ---
function login_function()
{
    $form = new HTML_QuickForm('login', 'POST');
    $form->addElement('text', 'username', 'User name:', 'size="10"');
    $form->addRule('username', 'Please enter your user name!', 'required',
                   null, 'client');
    $form->addElement('password', 'password', 'Password:');
    $form->addElement('submit', 'submit', 'Log In!');
    $form->display();
    exit;
}

One difference from the previous example is that a different Auth container (DB) is specified. The second parameter to the Auth constructor is container-specific, and in the case of Auth_Container_DB it contains an array with the DSN (data source name, DB's way of specifying a database to connect to), as well as which table and which fields in the table to use for looking up the username and password.

By default, Auth_Container_DB expects to find the password MD5-encoded, but you may specify any function for encoding the submitted password before comparing to the database value.

11.4.4. Auth Security Considerations

The biggest security issue with Auth is that it relies on PHP sessions. PHP sessions are secure by obscurity; the session id is secret, but at the same time, it is all a malicious user needs to compromise an account. This means you need to be extra careful not to expose the session id, so anything less than a network snoop does not reveal it.

To counter this, you can reduce the risk of session ids being stolen, and you can limit the usefulness of a stolen session id. This section offers some suggestions.

11.4.4.1 Auth Security Tip 1: Disable session.trans_sid

PHP's sesssion. trans_sid feature is meant to provide transparent sessions to users without cookies enabled. It will rewrite every link on the page to contain the session id as a GET parameter. Combined with Auth, this is the equivalent of putting the username and password in the URL.

With trans_sid enabled, there's a big risk of the session id leaking out because it will follow users clicking outgoing links through the HTTP Referer: header. This means that the session id may be logged on any web server to which that the Auth-protected site has links.

Some web servers are even so badly misconfigured that you can access their access logs through a browser. Hijacking the session is then just a matter of copying and pasting the URL from the access log.

By disabling trans_sid, you shut out users who do not have cookies installed, but eliminate the risk of session ids leaking out through the Referer header.

11.4.4.2 Auth Security Tip 2: Use Auth_HTTP

If you want to support users without cookies enabled, install the Auth_HTTP package. Auth_HTTP provides a wrapper around Auth that replaces the login form with a regular HTTP authentication pop-up window.

By using Auth_HTTP, you lose the logout functionality.

11.4.4.3 Auth Security Tip 3: Use HTTPS

Using HTTPS instead of HTTP protects usernames, passwords, and session ids from network packet sniffers. However, if an attacker has somehow obtained a session id, he may just as easily exploit it through HTTPS as through HTTP.

The major hurdle for most people is the cost and hassle involved in obtaining and maintaining an SSL certificate for their site, as well as the hosting cost which is often significant.

11.4.5. Auth Scalability Considerations

Because Auth uses sessions to keep track of logins and PHP stores sessions in local files by default, you will run into problems if you have a site that is being load balanced between several servers.

As an example, say that you have a site at www.example.com that is being load balanced between the servers www1 and www2. A user logs in, the POST request with the username and password hits www1, which stores the login information in the Auth session in local files. This means that if the same user submits another request that hits the www2 server, PHP can't find the Auth session on that machine, so it checks the login info through the Auth container and stores the login information in www2's local session files.

So far, so good; seen from the user's point of view, everything is working fine. However, you see two problems:

  • The load on the authentication back-end increases exponentially; the number of Auth checks will become N*M (sessions * servers), as opposed to just N (sessions) for a single-server setup.

  • If you set up sessions to idle-expire, you may experience erratic behavior because the same user could hit only www1 for a series of requests, and when he suddenly hits www2, his session could have expired on that server.

You can solve this in a number of ways, all of which have their pros and cons.

11.4.5.1 Auth Scalability Approach 1: Load-Balancing by Session Id

Use a load-balancing system that can use the PHP session id to distribute requests. This ensures that the same session keeps hitting the same server. The only (minor) disadvantage of this solution is that the session will be reset if the designated web server goes down and the load-balancing system sends the user to another server.

11.4.5.2 Auth Scalability Approach 2: Keep Session on Same Server

Redirect the user to a specific server once the Auth session is set up. In other words, send a Location: header back to the user redirecting him to www2.example.com for the remainder of his session. This is straightforward to implement, but it defeats any failover mechanisms because the user is sending requests directly to a specific server.

11.4.5.3 Auth Scalability Approach 3: Common Session Storage

Use a different session back-end that shares data between all the web servers. This could be everything from a regular database to a session-specific system like msession (available as a PHP extension; see the ext/msession directory in the PHP source tree).

11.4.6. Auth Summary

Auth is a versatile authentication package for the web environment. You have explored some of its functionality and learned about the advantages and challenges it presents.

    Team LiB
    Previous Section Next Section