Tuesday, July 14, 2009

Beginning PHP and Oracle From Novice to Professional by W. Jason Gilmore and Bob Bryla Chapter 14

Authenticating user identities is a common practice in today’s Web applications. This is done not only for security-related reasons but also to offer site customization features based on user prefer-
ences and type. Typically, users are prompted for a username and password, the combination of which forms a unique identifying value for that user. In this chapter, you’ll learn how to prompt for and validate this information using PHP’s built-in authentication capabilities. Specifically, in this chapter you’ll learn about the following:

• Basic HTTP-based authentication concepts

• PHP’s authentication variables, namely, $_SERVER['PHP_AUTH_USER'] and
$_SERVER['PHP_AUTH_PW']

• Several PHP functions that are commonly used to implement authentication procedures

• Three commonplace authentication methodologies, namely, hard-coding the login pair (username and password) directly into the script, file-based authentication, and database- based authentication
• Further restricting authentication credentials with a user’s Internet Protocol (IP) address

• Testing password “guessability” using the CrackLib extension

• Recovering lost passwords using one-time URLs

HTTP Authentication Concepts

HTTP offers a fairly effective means for user authentication. A typical authentication scenario proceeds like this:

1. The client requests a restricted resource.

2. The server responds to this request with a 401 (Unauthorized access) response message.

3. The client (browser) recognizes the 401 response and produces a pop-up authentication prompt similar to the one shown in Figure 14-1. Most modern browsers are capable of understanding HTTP authentication and offering appropriate capabilities, including Internet Explorer, Netscape Navigator, Mozilla, and Opera.

261

Figure 14-1. An authentication prompt

4. The user-supplied credentials (namely, the username and password) are sent back to the server for validation. If the user supplies correct credentials, access is granted; otherwise, it’s denied.
5. If the user is validated, the browser stores the authentication information within its authen- tication cache. This cache information remains within the browser until the cache is cleared or until another 401 server response is sent to the browser.

Although HTTP authentication effectively controls access to restricted resources, it does not secure the channel in which the authentication credentials travel. That is, it is fairly trivial for a well- positioned attacker to sniff, or monitor, all traffic taking place between a server and a client. Both the supplied username and the password are included in this traffic, both unencrypted. To eliminate the possibility of compromise through such a method, you need to implement a secure communica- tions channel, typically accomplished using Secure Sockets Layer (SSL). SSL support is available for all mainstream Web servers, including Apache and Microsoft’s Internet Information Services (IIS).

PHP Authentication

Integrating user authentication directly into your Web application logic is convenient and flexible; it’s convenient because it consolidates what would otherwise require some level of interprocess communication, and it’s flexible because integrated authentication provides a much simpler means for integrating with other components of an application, such as content customization and user privilege designation. For the remainder of this chapter, we’ll cover PHP’s built-in authentication feature and demonstrate several authentication methodologies that you can immediately begin incorporating into your applications.

Authentication Variables

PHP uses two predefined variables to authenticate a user: $_SERVER['PHP_AUTH_USER'] and
$_SERVER['PHP_AUTH_PW']. These variables store the two username and the password values, respec- tively. Although authenticating is as simple as comparing the expected username and password to these variables, you need to keep two important caveats in mind when using these predefined variables:

• Both variables must be verified at the start of every restricted page. You can easily accomplish this by authenticating the user prior to performing any other action on the restricted page, which typically means placing the authentication code in a separate file and then including that file in the restricted page using the require() function.
• These variables do not function properly with the CGI version of PHP, and they don’t function on Microsoft IIS. See the sidebar “PHP Authentication and IIS” for more information.

PHP AUTHENTICATION AND IIS

If you’re using IIS 6 or earlier in conjunction with PHP’s ISAPI module and you want to use PHP’s HTTP authentication capabilities, you need to make a minor modification to the examples offered throughout this chapter. The username and password variables are still available to PHP when using IIS, but not via $_SERVER['PHP_AUTH_USER']
and $_SERVER['PHP_AUTH_PW']. Instead, these values must be parsed from another server global variable,
$_SERVER['HTTP_AUTHORIZATION']. For example, you need to parse out these variables like so:

list($user, $pswd) =
explode(':', base64_decode(substr($_SERVER['HTTP_AUTHORIZATION'], 6)));

If you’re running IIS 7 or newer, forms authentication is no longer restricted to ASP.NET pages, meaning you’re able to properly protect your PHP-driven applications. Consult the IIS 7 documentation for more on this matter.

Useful Functions

Two standard functions are commonly used when handling authentication via PHP: header() and
isset(). We introduce both in the following sections.

Sending an HTTP Header to the Browser

The header() function sends a raw HTTP header to the browser. Its prototype follows:

void header(string string [, boolean replace [, int http_response_code]])

The string parameter specifies the header information sent to the browser. The optional replace parameter determines whether this information should replace or accompany a previously sent header. Finally, the optional http_response_code parameter defines a specific response code that will accompany the header information. Note that you can include this code in the string, as we will soon demonstrate. Applied to user authentication, this function is useful for sending the WWW authentication header to the browser, causing the pop-up authentication prompt to be displayed. It is also useful for sending the 401 header message to the user if incorrect authentication credentials are submitted. An example follows:

<?php
header('WWW-Authenticate: Basic Realm="Book Projects"');
header("HTTP/1.1 401 Unauthorized");
?>

Note that unless output buffering is enabled, these commands must be executed before any
output is returned. Neglecting this rule will result in a server error, because of the violation of the
HTTP specification.

Determining Whether a Variable Has Been Assigned

The isset() function determines whether a variable has been assigned a value. It returns TRUE if the variable contains a value, and it returns FALSE if it does not. Its prototype follows:

boolean isset(mixed var [, mixed var [,...]])

As applied to user authentication, the isset() function is useful for determining whether the
$_SERVER['PHP_AUTH_USER'] and $_SERVER['PHP_AUTH_PW'] variables are properly set. Listing 14-1 offers a usage example.

Listing 14-1. Using isset() to Verify Whether a Variable Contains a Value

<?php

// If the username or password isn't set, display the authentication window
if (! isset($_SERVER['PHP_AUTH_USER']) || ! isset($_SERVER['PHP_AUTH_PW'])) {
header('WWW-Authenticate: Basic Realm="Authentication"');
header("HTTP/1.1 401 Unauthorized");

// If the username and password are set, output their credentials
}else {
echo "Your supplied username: ".$_SERVER['PHP_AUTH_USER']."<br />";
echo "Your password: ".$_SERVER['PHP_AUTH_PW']."<br />";
}
?>

PHP Authentication Methodologies

You can implement authentication via a PHP script in several ways. In doing so, you should always consider the scope and complexity of your authentication needs. The following sections discuss hard-coding a login pair directly into the script by using file-based authentication, by using IP-based authentication, and by using database-based authentication. Please examine each approach, and choose a solution that best fits your needs.

Hard-Coded Authentication

The simplest way to restrict resource access is by hard-coding the username and password directly in the script. Listing 14-2 offers an example of how to accomplish this.

Listing 14-2. Authenticating Against a Hard-Coded Login Pair

if (($_SERVER['PHP_AUTH_USER'] != 'specialuser') || ($_SERVER['PHP_AUTH_PW'] != 'secretpassword')) {
header('WWW-Authenticate: Basic Realm="Secret Stash"');
header('HTTP/1.0 401 Unauthorized');
print('You must provide the proper credentials!');
exit;
}

In this example, if $_SERVER['PHP_AUTH_USER'] and $_SERVER['PHP_AUTH_PW'] are equal to
specialuser and secretpassword, respectively, the code block will not execute, and anything ensuing that block will execute. Otherwise, the user is prompted for the username and password until either

the proper information is provided or a 401 (Unauthorized access) response message is displayed because of multiple authentication failures.
Although authentication against hard-coded values is very quick and easy to configure, it has several drawbacks. Foremost, all users requiring access to that resource must use the same authen- tication pair. In most real-world situations, each user must be uniquely identified so user-specific preferences or resources can be provided. Second, you can change the username or password only by entering the code and making the manual adjustment. The next two methodologies remove these issues.

File-Based Authentication

Often you need to provide each user with a unique login pair, making it possible to log user-specific login times, movements, and actions. This is easily accomplished with a text file, much like the one commonly used to store information about Unix users (/etc/passwd). Listing 14-3 offers such a file. Each line contains a username and an encrypted password pair, with the two elements separated by a colon (:).

Listing 14-3. The authenticationFile.txt File Containing Encrypted Passwords

jason:60d99e58d66a5e0f4f89ec3ddd1d9a80 donald:d5fc4b0e45c8f9a333c0056492c191cf mickey:bc180dbc583491c00f8a1cd134f7517b
A crucial security consideration regarding authenticationFile.txt is that this file should be stored outside the server document root. If it is not, an attacker could discover the file through brute- force guessing, revealing half the login combination. In addition, although you have the option to skip password encryption, this practice is strongly discouraged, because users with access to the server might be able to view the login information if file permissions are not correctly configured.
The PHP script required to parse this file and authenticate a user against a given login pair is only a tad more complicated than the script used to authenticate against a hard-coded authentica- tion pair. The difference lies in the script’s additional duty of reading the text file into an array and then cycling through that array searching for a match. This involves the use of several functions, including the following:

• file(string filename): The file() function reads a file into an array, with each element of the array consisting of a line in the file.
• explode(string separator, string string [, int limit]): The explode() function splits a string into a series of substrings, with each string boundary determined by a specific separator.
• md5(string str): The md5() function calculates an MD5 hash of a string, using RSA Data
Security Inc.’s MD5 Message-Digest algorithm (http://www.rsa.com/).

■Note Although they are similar in function, you should use explode() instead of split(), because split()
is a tad slower due to its invocation of PHP’s regular expression parsing engine.

Listing 14-4 illustrates a PHP script that is capable of parsing authenticationFile.txt, poten- tially matching a user’s input to a login pair.

Listing 14-4. Authenticating a User Against a Flat-File Login Repository

<?php

// Preset authentication status to FALSE
$authorized = FALSE;

if (isset($_SERVER['PHP_AUTH_USER']) && isset($_SERVER['PHP_AUTH_PW'])) {

// Read the authentication file into an array
$authFile = file("/usr/local/lib/php/site/authenticate.txt");

// Search array for authentication match if (in_array($_SERVER['PHP_AUTH_USER'].
":"
.md5($_SERVER['PHP_AUTH_PW'])."\n", $authFile))
$authorized = TRUE;
}

// If not authorized, display authentication prompt or 401 error if (! $authorized) {
header('WWW-Authenticate: Basic Realm="Secret Stash"');
header('HTTP/1.0 401 Unauthorized');
print('You must provide the proper credentials!');
exit;
}
// restricted material goes here...
?>

Although the file-based authentication system works great for relatively small, static authenti-
cation lists, this strategy can become somewhat inconvenient when you’re handling a large number of users; when users are regularly being added, deleted, and modified; or when you need to incorpo- rate an authentication scheme into a larger information infrastructure (into a preexisting user table, for example). Such requirements are better satisfied by implementing a database-based solution. The following section demonstrates just such a solution—using a database to store authentication pairs.

Database-Based Authentication

Of all the various authentication methodologies discussed in this chapter, implementing a database- based solution is the most powerful, because it not only enhances administrative convenience and scalability but also can be integrated into a larger database infrastructure. For the purposes of this example, we’ll limit the data store to four fields—a primary key, the user’s name, a username, and a password. These columns are placed into a table called userauth, shown in Listing 14-5.

■Note If you’re unfamiliar with Oracle and are confused by the syntax in Listing 14-5, consider reviewing Chapter 32.

Listing 14-5. A User Authentication Table

CREATE SEQUENCE userauth_seq start with 1
increment by 1 nomaxvalue;

CREATE TABLE userauth (
userauth_id NUMBER PRIMARY KEY, common_name VARCHAR2(35) NOT NULL, username VARCHAR2(8) NOT NULL,
pswd VARCHAR2(32) NOT NULL
);

Table 14-1 shows some sample data.

Table 14-1. Sample userauth Table Data

userauth_id common_name username pswd
1 Jason Gilmore wjgilmor 54b0c58c7ce9f2a8b551351102ee0938
2 Bob Bryla bbryla 416473c65bd22518605b1c27021b1a26
3 Matt Wade mwade 0f4bab08f2f769252cfbbddfb97e58e7

Listing 14-6 displays the code used to authenticate a user-supplied username and password against the information stored within the userauth table.

Listing 14-6. Authenticating a User Against an Oracle Database

<?php
// Create a function for displaying the authentication prompt function authenticate_user() {
header('WWW-Authenticate: Basic realm="Secret Stash"');
header("HTTP/1.0 401 Unauthorized");
exit;
}

// If no username or password provided, authenticate

if (! isset($_SERVER['PHP_AUTH_USER']) || ! isset($_SERVER['PHP_AUTH_PW'])) {
authenticate_user();
} else {

// Connect to the Oracle database
$conn = oci_connect('WEBUSER', 'oracle123', '//127.0.0.1/XE')
or die("Can't connect to database server!");

// Convert the provided password into a hash
$pswd = md5($_SERVER['PHP_AUTH_PW']);

// Create query
$query = "SELECT username, pswd FROM userauth
WHERE username=:username AND pswd=:pswd";

// Prepare statement
$stmt = oci_parse($conn, $query);

// Bind PHP variables
oci_bind_by_name($stmt, ':username', $_SERVER['PHP_AUTH_USER'], 8);
oci_bind_by_name($stmt, ':pswd', $pswd, 32);

// Execute statement oci_execute($stmt);

// Has a row been returned?
list($username, $pswd) = oci_fetch_array($stmt, OCI_NUM);

// If no row, attempt to authenticate anew if ($username == "") {
authenticate_user();
} else {
echo "Welcome to the secret zone!";
}
}
?>

Although database authentication is more powerful than the previous two methodologies, it is
really quite trivial to implement. Simply execute a selection query against the userauth table using the entered username and password as criteria for the query. Of course, such a solution is not depen- dent upon the specific use of a MySQL database; you could use any relational database in its place.

IP-Based Authentication

Sometimes you need an even greater level of access restriction to ensure the validity of the user. Of course, a username/password combination is not foolproof; this information can be given to someone else or can be stolen from a user. It could also be guessed through deduction or brute force, particu- larly if the user chooses a poor login combination, which is still quite common. To combat this, one effective way to further enforce authentication validity is to require not only a valid username/password login pair but also a specific IP address. To do so, you need to only slightly modify the userauth table used in the previous section, and you need to modify the query used in Listing 14-6. Listing 14-7 displays the revised table.

Listing 14-7. The userauth Table Revisited

CREATE TABLE userauth (
userauth_id NUMBER PRIMARY KEY, common_name VARCHAR2(35) NOT NULL, username VARCHAR2(8) NOT NULL,
pswd CHAR(32) NOT NULL,
ipaddress VARCHAR2(15) NOT NULL
);

Listing 14-8 displays the code for validating the username, password, and IP address.

Listing 14-8. Authenticating Using a Login Pair and an IP Address

<?php
// Create a function for displaying the authentication prompt function authenticate_user() {
header('WWW-Authenticate: Basic realm="Secret Stash"');
header("HTTP/1.0 401 Unauthorized");
exit;
}

// If no provided username or password, authenticate

if (! isset($_SERVER['PHP_AUTH_USER']) || ! isset($_SERVER['PHP_AUTH_PW'])) {
authenticate_user();
} else {

// Connect to the Oracle database
$conn = oci_connect('WEBUSER', 'oracle123', '//127.0.0.1/XE')
or die("Can't connect to database server!");

// Convert the provided password into a hash
$pswd = md5($_SERVER['PHP_AUTH_PW']);

// Create query
$query = "SELECT username, pswd FROM userauth
WHERE username=:username
AND pswd=:pswd
AND ipaddress=:ip";

// Prepare statement
$stmt = oci_parse($conn, $query);

// Bind the values
oci_bind_by_name($stmt, ':username', $_SERVER['PHP_AUTH_USER'], 8);
oci_bind_by_name($stmt, ':pswd', $pswd, 32);
oci_bind_by_name($stmt, ':ip', $_SERVER['REMOTE_ADDR'], 15);

// Execute statement oci_execute($stmt);

// Has a row been returned?
list($username, $pswd) = oci_fetch_array($stmt, OCI_NUM);

// If no row, attempt to authenticate anew if ($username == "") {
authenticate_user();
} else {
echo "Welcome to the secret zone!";
}
}
?>

Although this additional layer of security works quite well, keep in mind it is not foolproof. The practice of IP spoofing, or tricking a network into thinking that traffic is emanating from a particular IP address, has long been a tool in the savvy attacker’s toolbox. Therefore, if such an attacker gains access to a user’s username and password, they could conceivably circumvent your IP-based security obstacles.

User Login Administration

When you incorporate user logins into your application, providing a sound authentication mecha- nism is only part of the total picture. How do you ensure that the user chooses a sound password of sufficient difficulty that attackers cannot use it as a possible attack route? Furthermore, how do you deal with the inevitable event of the user forgetting his password? We cover both topics in detail in the following sections.

Testing Password Guessability with the CrackLib Library

In an ill-conceived effort to prevent forgetting their passwords, users tend to choose something easy to remember, such as the name of their dog, their mother’s maiden name, or even their own name or age. Ironically, this practice often doesn’t help users remember the password and, even worse, offers attackers a rather simple route into an otherwise restricted system, either by researching the user’s background and attempting various passwords until the correct one is found or by using brute force to discern the password through numerous repeated attempts. In either case, the password typically is broken because the user has chosen a password that is easily guessable, resulting in the possible compromise of not only the user’s personal data but also the system itself.
Reducing the possibility that such easily guessable passwords could be introduced into the system is quite simple by turning the procedure of unchallenged password creation into one of auto- mated password approval. PHP offers a wonderful means for doing so via the CrackLib library, created by Alec Muffett (http://www.crypticide.com/dropsafe/). CrackLib is intended to test the strength of a password by setting certain benchmarks that determine its guessability:

• Length: Passwords must be longer than four characters.

• Case: Passwords cannot be all lowercase.

• Distinction: Passwords must contain adequate different characters. In addition, the password cannot be blank.
• Familiarity: Passwords cannot be based on a word found in a dictionary. In addition, the pass- word cannot be based on a reversed word found in the dictionary. Dictionaries are discussed further in a bit.
• Standard numbering: Because CrackLib’s author is British, he thought it a good idea to check against patterns similar to what is known as a National Insurance (NI) number. The NI number is used in Britain for taxation, much like the Social Security number (SSN) is used in the United States. Coincidentally, both numbers are nine characters long, allowing this mechanism to efficiently prevent the use of either, if a user is dense enough to use such a sensitive identifier for this purpose.

Installing PHP’s CrackLib Extension

To use the CrackLib extension, you need to first download and install the CrackLib library, available at http://www.crypticide.com/dropsafe/info/home.html. If you’re running a Linux/Unix variant, it

might already be installed, because CrackLib is often packaged with these operating systems. Complete installation instructions are available in the README file found in the CrackLib TAR package.
PHP’s CrackLib extension was unbundled from PHP as of version 5 and was moved to the PHP Extension Community Library (PECL), a repository for PHP extensions. Therefore, to use CrackLib, you need to download and install the CrackLib extension from PECL (http://pecl.php.net/).
Once you install CrackLib, you need to make sure the crack.default_dictionary directive in php.ini is pointing to a password dictionary. Such dictionaries abound on the Internet, so executing a search will turn up numerous results. Later in this chapter you’ll learn more about the various types of dictionaries at your disposal.

Using the CrackLib Extension

Using PHP’s CrackLib extension is quite easy. Listing 14-9 offers a complete usage example.

Listing 14-9. Using PHP’s CrackLib Extension

<?php
$pswd = "567hejk39";

// Open the dictionary. Note that the dictionary
// file name does NOT include the extension.
$dictionary = crack_opendict('/usr/lib/cracklib_dict');

// Check password for guessability
$check = crack_check($dictionary, $pswd);

// Retrieve outcome
echo crack_getlastmessage();

// Close dictionary crack_closedict($dictionary);
?>

In this particular example, crack_getlastmessage() returns the string strong password because
the password denoted by $pswd is sufficiently difficult to guess. However, if the password is weak, one of a number of different messages could be returned. Table 14-2 offers a few other passwords and the resulting outcome from passing them through crack_check().

Table 14-2. Password Candidates and the crack_check() Function’s Response

Password Response

Mary it is too short

12 it's WAY too short

1234567 it is too simplistic/systematic

street it does not contain enough DIFFERENT characters

By writing a short conditional statement, you can create user-friendly, detailed responses based on the information returned from CrackLib. Of course, if the response is strong password, you can allow the user’s password choice to take effect.

Dictionaries

Listing 14-11 uses the cracklib_dict.pwd dictionary, which is generated by CrackLib during the installation process. Note that in the example, the extension .pwd is not included when referring to the file. This seems to be a quirk with the way PHP wants to refer to this file and could change some time in the future so that the extension is also required.
You are also free to use other dictionaries, of which many are freely available on the Internet. Furthermore, you can find dictionaries for practically every spoken language. One particularly complete repository of such dictionaries is available on the University of Oxford’s FTP site at ftp://ftp.ox.ac.uk. In addition to quite a few language dictionaries, the site offers a number of interesting specialized dictionaries, including one containing keywords from many Star Trek plot summaries. At any rate, regardless of the dictionary you decide to use, simply assign its location to the crack.default_dictionary directive, or open it using crack_opendict().

One-Time URLs and Password Recovery

As sure as the sun rises, your application users will forget their passwords. All of us are guilty of forgetting such information, and it’s not entirely our fault. Take a moment to list all the different login combina- tions you regularly use; our guess is that you have at least 12 such combinations. E-mail, workstations, servers, bank accounts, utilities, online commerce, securities and mortgage brokerages . . . we use passwords to manage nearly everything these days. Because your application will assumedly be adding yet another login pair to the user’s list, you should put a simple, automated mechanism in place for retrieving or resetting the user’s password should it be forgotten. Depending on the sensi- tivity of the material protected by the login, retrieving the password might require making a phone call or sending the password via the postal service. As always, use discretion when you devise mech- anisms that may be exploited by an intruder. This section examines one such mechanism, referred to as a one-time URL.
A one-time URL is commonly given to a user to ensure uniqueness when no other authentica-
tion mechanisms are available or when the user would find authentication perhaps too tedious for the task at hand. For example, suppose you maintain a list of newsletter subscribers and want to know which and how many subscribers are actually reading each monthly issue. Simply embedding the newsletter in an e-mail won’t do, because you would never know how many subscribers were simply deleting the e-mail from their inboxes without even glancing at the contents. Rather, you could offer them a one-time URL pointing to the newsletter, one of which might look like this:

http://www.example.com/newsletter/0503.php?id=9b758e7f08a2165d664c2684fddbcde2

In order to know exactly which users showed interest in the newsletter issue, a unique ID parameter like the one shown in the preceding URL has been assigned to each user and stored in some subscriber table. Such values are typically pseudorandom, derived using PHP’s md5() and uniqid() functions, like so:

$id = md5(uniqid(rand(),1));

The subscribers table might look something like the following:

CREATE SEQUENCE subscribers_seq start with 1
increment by 1 nomaxvalue;

CREATE TABLE subscribers ( subscriber_ID NUMBER PRIMARY KEY, email VARCHAR2(55) NOT NULL, uniqueid VARCHAR2(32) NOT NULL,
read_newsletter CHAR(1) DEFAULT 'N' CHECK (read_newsletter IN ('Y', 'N'))
);

When the user clicks this link, causing the newsletter to be displayed, the following code could
execute before displaying the newsletter:

$query = "UPDATE subscribers SET read_newsletter='Y' WHERE uniqueid=:id";

// Prepare statement
$stmt = oci_parse($conn, $query);

oci_bind_by_name($stmt, ':id', $_GET['id'], 32, SQL_INT);

// Execute statement oci_execute($stmt);

The result is that you will know exactly how many subscribers showed interest in the newsletter, because they all actively clicked the link.
You can apply this same concept to password recovery. To illustrate how you accomplish this, consider the revised userauth table shown in Listing 14-10.

Listing 14-10. A Revised userauth Table

CREATE TABLE userauth (
userauth_id NUMBER PRIMARY KEY, common_name VARCHAR2(35) NOT NULL, email VARCHAR2(55) NOT NULL, username VARCHAR2(8) NOT NULL,
pswd CHAR(32) NOT NULL,
unique_identifier CHAR(32) NOT NULL
);

Suppose one of the users found in this table forgets his password and thus clicks the “Forgot
password?” link, commonly found near a login prompt. The user will arrive at a page on which he is asked to enter his e-mail address. Upon entering the address and submitting the form, a script is executed similar to that shown in Listing 14-11.

Listing 14-11. A One-Time URL Generator

<?php

// Connect to the Oracle database
$conn = oci_connect('WEBUSER', 'oracle123', '//127.0.0.1/XE')
or die("Can't connect to database server!");

// Create unique identifier
$id = md5(uniqid(rand(),1));

// Filter the e-mail address
$emailaddr = htmlentities($_POST['email']);

// Set user's uniqueidentifier field to a unique id.
$query = "UPDATE userauth SET unique_identifier=:id WHERE email=:email";

// Prepare statement
$stmt = oci_parse($conn, $query);

// Bind the values oci_bind_by_name($stmt, ':id', $id, 32); oci_bind_by_name($stmt, ':email', $emailaddr, 55);

// Execute statement oci_execute($stmt);

// Create the e-mail
$email = <<<email
Dear user,
Click on the following link to reset your password: http://www.example.com/users/lostpassword.php?id=$id email;

// E-mail user password reset options mail($emailaddr,"Password recovery",
"$email","FROM:services@example.com");
echo "<p>Instructions regarding resetting your password have been sent to {$emailaddr}</p>";
?>

When the user receives this e-mail and clicks the link, the script lostpassword.php executes, as
shown in Listing 14-12.

Listing 14-12. Resetting a User’s Password

<?php
// Create a pseudorandom password five characters in length
$pswd = substr(md5(uniqid(rand(),1),5));

// Filter the passed user ID
$id = htmlentities($_GET['id']);

// Update the user table with the new password.
$query = "UPDATE userauth SET pswd=:pswd WHERE unique_identifier=:id";

// Prepare statement
$stmt = oci_parse($conn, $query);

// Bind the values oci_bind_by_name($stmt, ':id', $id); oci_bind_by_name($stmt, ':pswd', $pswd, 5);

// Execute statement oci_execute($stmt);

// Display the new password to the user
echo "<p>Your password has been reset to $pswd. Please log in and change your password to one of your liking.</p>";
?>

Of course, this is only one of many recovery mechanisms. For example, you could use a similar
script to provide the user with a form for resetting his own password.

Summary

This chapter introduced PHP’s authentication capabilities, features that are practically guaranteed to be incorporated into many of your future applications. In addition to discussing the basic concepts surrounding this functionality, we covered several common authentication methodologies, including authenticating against hard-coded values, file-based authentication, and database-based authentication. We also talked about decreasing password “guessability” using PHP’s CrackLib extension and discussed how to recover passwords using one-time URLs.
The next chapter discusses another popular PHP feature—handling file uploads via the browser.

0 comments: