- User Authentication Overview
- Generating Passwords
- Authenticating Users Against Text Files
- Authenticating Users by IP Address
- Authenticating Users Using HTTP Authentication
- Authenticating Users by Database Query
Authenticating Users Using HTTP Authentication
PHP has some built-in support for basic "HTTP Authentication." HTTP authentication is the type of authentication in which a small window pops up in front of the browser prompting you for a username and password. Figure 7-2 and Figure 7-3 show HTTP Authentication windows in Internet Explorer and Mozilla for Linux, respectively. HTTP authentication is server-dependent, the most common example of which is Apache's HTTP authentication method. The methods described here assume you are using an Apache server.
Figure 7-2. HTTP Authentication Login Window in Internet Explorer
Figure 7-3. HTTP Authentication Window in Mozilla on Linux
Typically, the HTTP Authentication method under Apache is defined in .htaccess files.1 A .htaccess file usually restricts an entire directory. However, PHP provides a way to use Apache based HTTP Authentication without having to define .htaccess files for the same type of authentication. Additionally, you can easily use PHP's HTTP Authentication method on a per page basis.
Using the PHP method, you can also use your own password scheme. Under Apache, you typically placed encrypted passwords in a password file (usually called .htpasswd). Using the PHP method, you can place your passwords in a file, in a database, or any other method. You can also use any encryption scheme you want, whereas Apache is restricted to UNIX crypted passwords, their own flavor of MD5 passwords, or plain text.
However, if users forget their passwords, you cannot send it to them, because you only know the encrypted version. You either have to reset the password or provide a hint for the password that may help the users remember what it is they used for the password. More on this later in the chapter.
The next script in this chapter details how to use PHP's method of HTTP Authentication with an Apache server. The script also allows you to use passwords encrypted with the crypt() function (which are portable and can be used with Apache's default HTTP Authentication method) or use the basic PHP md5() function to encrypt passwords. The latter option is useful if you already have several applications that use MD5 encryption for passwords.
Script 7-4 http_authentication.php
1. <? 2. function check_pass($login, $password, $mode) { 3. global $password_file; 4. if(!$fh = fopen($password_file, "r")) { die("<P>Could Not Open Password File"); } 5. $match = 0; 6. while(!feof($fh)) { 7. $line = fgets($fh, 4096); 8. $from_file = explode(":", $line); 9. if($from_file[0] == $login) { 10. if($mode == "crypt"){ 11. $salt = substr($from_file[1],0,2); 12. $user_pass = crypt($password,$salt); 13. } elseif ($mode == "md5") { 14. $user_pass = md5($password); 15. } 16. if(rtrim($from_file[1]) == $user_pass) { 17. $match = 1; 18. break; 19. } 20. } 21. } 22. if($match) { 23. return 1; 24. } else { 25. return 0; 26. } 27. fclose($fh); 28. } 29. function authenticate() { 30. Header("WWW-Authenticate: Basic realm=\"RESTRICTED ACCESS\""); 31. Header("HTTP/1.0 401 Unauthorized"); 32. echo ("<h1>INVALID USERNAME OR PASSWORD. ACCESS DENIED<h1>"); 33. exit; 34. } 35. /*** MAIN ***/ 36. //select md5 or crypt for $mode. md5 is for md5 encoded passwords, crypt is for passwords encoded using apache's httpasswd 37. $mode = "md5"; 38. $password_file = "C:/apache/passwords/pass.txt"; 39. if (!isset($PHP_AUTH_USER)) { 40. authenticate(); 41. } else { 42. if(check_pass($PHP_AUTH_USER, $PHP_AUTH_PW, $mode)) { 43. ?> 44. <h1>ACCEPTED</h1> 45. <? 46. } else { 47. authenticate(); 48. } 49. } 50. ?>
Script 7-4. http_authentication.php Line-by-Line Explanation
LINE |
DESCRIPTION |
2 |
Define a function called check_pass() that takes three arguments:
|
3 |
Allow $password_file to be used globally by this function. |
4 |
Attempt to open the file in read mode and assign a file handle to it. If the file cannot be opened, then print an error and kill the script. |
5 |
Define the variable $match and set it to false (0). |
6–21 |
Create a for loop that loops through the file until the EOF is reached. |
7 |
Read in one line from the file. |
8 |
Explode the contents of the line into an array called $from_file. This should create an array with two items. The first is the username, and the second is the password. |
9 |
If the first element in the array (the username) matches the user-entered username ($login), continue to line 10. Otherwise, go back to line 6 and loop again. |
10 |
Check to see if the $mode variable that was passed to this function is "crypt." If it is, continue to line 11. Otherwise, go to line 13. |
11 |
Define the salt required by the crypt() function. This script can be used with Apache htpasswd generated passwords, so it has to use Apache's method of using crypt()s, which is to use the first two characters of the encrypted password as the salt. This line uses the substr() function to pull out the first two characters from the password obtained from the file and place the value in the $salt variable. |
12 |
Encrypt the user-entered password with the crypt() function using the salt obtained from the previous line. Continue to line 16. |
13 |
Check to see if the $mode variable that was passed to this function is "md5". If it is, continue to line 14. Note: The function assumes that $mode is either "crypt" or "md5". If it is neither, then the script loops through the entire file without checking anything and returns no matches, which in turn means that authentication fails. It may be useful to print an error message that $mode was not set to either of the required values, but that won't help a user who is trying to access your site. For debugging, you may want to optionally add another else statement that states that $mode was neither "crypt" nor "md5". |
14 |
Encrypt the user-entered password with the md5() function. Continue to line 16. |
16–17 |
Trim any whitespace from the password obtained from the file and compare it to the encrypted version of the user-entered password. If they match, then set the $match variable to true (1). |
18 |
Break out of the loop, since you found a valid match. |
19 |
Close the if statement that checks the password. |
20 |
Close the if statement that checks the username. |
21 |
Close the for loop. |
22–23 |
If $match is true (1), then return true. |
24–26 |
If $match is false(0), then return false. |
27 |
Close the file. |
28 |
End the function declaration. |
29 |
Create a function called authenticate() to display the HTTP Authentication login screen to users if they have not been previously authenticated. |
30–31 |
Use the header() function to send authentication headers to a user's browser. Line 30 attempts to obtain the username/password from the user. Line 31 is executed if the users have already submitted their username/password combination and there was no corresponding match in the password file. |
32 |
Print a message to the screen notifying the users that their username/password combination is invalid. |
33 |
Exit from the script. |
34 |
End the function declaration. |
35 |
Begin the main program. |
36 |
Include a comment in the script as to the nature of the $mode variable. |
37 |
Assign the $mode variable the value "md5" (or "crypt" if your passwords are encrypted using the crypt() function). |
38 |
Assign the location of the password file to the $password_file variable. |
39 |
Check to see if the global variable $PHP_AUTH_USER has been set. $PHP_AUTH_USER is a special PHP variable that is used with HTTP Authentication. This is the variable that is obtained when the user logs in using the HTTP Authentication window. |
40 |
If $PHP_AUTH_USER variable has not been set, then execute the authenticate() function. |
41–42 |
If the $PHP_AUTH_USER variable has been set, then execute the check_pass() function using the $PHP_AUTH_USER, $PHP_AUTH_PW, and $mode variables as arguments. The $PHP_AUTH_PW variable is the special PHP global variable obtained from the HTTP Authentication login window. |
43–45 |
If the check_pass() function returns true (1), then print out "ACCEPTED" to the screen. You would normally provide your protected content here. |
46–48 |
If the check_pass() function returns false, execute the authenticate() function again. The users have three chances to correctly enter their username and password. After the third failure, the script returns lines 31 and 32. |
49 |
Close the if statement started on line 39. |
50 |
End the script. |