- 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 Against Text Files
Not all sites contain a database back-end to store data, and some sites probably will never require them. Still, you may want to limit access to certain parts of the site or even the entire site. One way to do this is to authenticate against a text file stored someplace on the server, preferably out of the Web server directory so that there is no way it can be accessed by someone with a Web browser.
This method only requires the standard file functions, as well as the MD5 encryption function.
This script serves a dual purpose. It allows you to add users to a password file, as well as test authentication. This way, you can create a blank text file and easily fill it with some username/password combinations so that you can test the functionality of the script.
This script is not meant to be used in its entirety to check passwords on a site. You would need to remove the functions that allow you to add users before you placed the script in your own site.
Script 7-2 file_authentication.php
1. <? 2. $password_file = "C:/apache/passwords/pass.txt"; 3. 4. function check_pass($login, $password) { 5. global $password_file; 6. if(!$fh = fopen($password_file, "r")) {die("<P>Could Not Open Password File");} 7. $match = 0; 8. $password = md5($password); 9. while(!feof($fh)) { 10. $line = fgets($fh, 4096); 11. $user_pass = explode(":", $line); 12. if($user_pass[0] == $login) { 13. if(rtrim($user_pass[1]) == $password) { 14. $match = 1; 15. break; 16. } 17. } 18. } 19. if($match) { 20. return 1; 21. } else { 22. return 0; 23. } 24. fclose($fh); 25. } 26. 27. function print_login_form($login) { 28. ?> 29. <p>Please Log In: 30. <form action=file_authentication.php method=post> 31. <p>Login: <input type="text" name="login" value="<?=$login?>"> 32. <br>Password: <input type="password" name="password"> 33. <br><input type="submit" name="checkpass" value="Login!"> 34. </form> 35. <? 36. } 37. 38. function print_add_form() { 39. ?> 40. <p>Add New User: 41. <form action=file_authentication.php method=post> 42. <p>Login: <input type="text" name="adduser"> 43. <br>Password: <input type="password" name="addpass"> 44. <br><input type="submit" name="add" value="Add User!"> 45. </form> 46. <? 47. } 48. 49. function add_user($adduser, $addpass) { 50. global $password_file; 51. if(!$fh = fopen($password_file, "a+")) { die("<P>Could Not Open Password File"); } 52. rewind($fh); 53. while(!feof($fh)) { 54. $line = fgets($fh, 4096); 55. $user_pass = explode(":", $line); 56. if($user_pass[0] == $adduser) { 57. echo "<h2>Duplicate Login. Invalid!</h2>"; 58. return 0; 59. } 60. } 61. $add = $adduser . ":" . md5($addpass) . "\n"; 62. if(!fwrite($fh, $add)) { die("<P>Could Not Write To Password File"); } 63. fclose($fh); 64. echo"<h2>User Added!</h2>"; 65. } 66. /***** MAIN *****/ 67. if(isset($checkpass)) { 68. if(check_pass($login, $password)) { 69. echo "<h2>Login Success!</h2>"; 70. } else { 71. echo "<h2>Login Failed</h2><p>Bad username or password. Login and Password are case-sensitive. Try again:"; 72. print_login_form($login); 73. } 74. } elseif(isset($add_form)) { 75. print_add_form(); 76. } elseif(isset($add)) { 77. add_user($adduser, $addpass); 78. } else { 79. print_login_form(""); 80. } 81. ?> 82. <p>You can <a href=file_authentication.php?add_form=1>Add Users</a> or <a href=file_authentication.php>login</a> an existing user.
Script 7-2. file_authentication.php Line-by-Line Explanation
LINE |
DESCRIPTION |
2 |
Define the file that contains the passwords. Note that you should specify the correct path on your machine that points to the password file. On *nix-based systems, you'd obviously omit the "C:\" nonsense in the path. |
4 |
Create a function that checks the user-entered password against the username/password combination stored in the password file. |
5 |
Allow the password file to be read and modified globally by this function. |
6 |
Attempt to open the file and assign it to a file pointer. If the file cannot be opened, then print an error and exit from the script. |
7 |
Initialize a variable to track a username/password match. Initialize it to false (0) to assume the password is incorrect. |
8 |
Since the passwords are stored encrypted, encrypt the user-entered password using the MD5 function. |
9–18 |
Create a while loop that searches through the file until the end of file (EOF) is reached. |
10 |
For each iteration of the loop, get one line from the file and place it in the $line variable. |
11 |
Create an array of the line obtained from the file using the explode function. Since the usernames and passwords are stored in the format: USER:PASS and the explode() function breaks up the line on every ":", the script places two items into the $user_pass array: the username at index [0] and the password at index [1]. |
12 |
Check if the username from the file, which is stored in $user_pass[0], matches the username entered by the user, $login. |
13 |
If the user-entered username matches the username obtained from the file, then check to see if the password from the file, $user_pass[1], matches the password entered by the user, $password. |
14 |
If the passwords match, then set the $match variable to true (1), since the user-entered username and password match the ones contained in the password file. |
15 |
If the passwords match, then break from the loop. |
16 |
Close out the password-checking if statement. |
17 |
Close out the username-checking if statement. |
18 |
Close out the while loop. |
19–20 |
If there was a match, then return true to the calling program or function. |
21–23 |
If there was no match, then return false to the calling program or function. |
24 |
Close the file. |
25 |
End the function declaration. |
27 |
Create a function that prints a login form to the screen. The function takes one argument, called $login, which is used to automatically re-enter the username if there was no match from a previous attempt at logging in. |
28 |
Stop parsing the page as PHP. |
29–30 |
Begin displaying the login form. |
31 |
Print the username field. If this is a subsequent attempt to log in, then automatically enter the username. |
32–34 |
Continue printing the form to the page. |
35 |
Start parsing the page as PHP again. |
36 |
End the function declaration. |
38 |
Create a function that prints a form allowing you to add username/password combinations to the password file. |
39–46 |
Stop parsing the page as PHP and print out a standard HTML form, then continue parsing the page as PHP. |
47 |
End the function declaration. |
49 |
Create a function that adds a username and password combination to the password file. The function takes two arguments: $adduser and $addpass. |
50 |
Allow the password file to be read and modified globally by this function. |
51 |
Attempt to open the file and assign it to a file pointer. If the file cannot be opened, then print an error and exit from the script. |
52 |
Since we opened the file using append mode ("a+") on line 51, rewind the file so that the file pointer is at the beginning of the file. The append mode places the file pointer at the end of the file by default. |
53–60 |
Create a while loop that searches through the file until the end of file (EOF) is reached. |
54 |
For each iteration of the loop, get one line from the file and place it in the $line variable. |
55 |
Create an array of the line obtained from the file using the explode function. |
56 |
Check to see if the username stored in $user_pass[0] is the same as the username entered into the Add User form. Usernames should always be unique! This line checks to make sure that a duplicate username is not being entered into the password file. |
57 |
If the usernames match, then you are trying to enter a duplicate username. Print out a message to the users informing them that the username they entered is invalid. |
58 |
Return false to the calling program or function. |
61 |
Since this line has been reached, it means that the username entered was unique (if it was not, then the function would have terminated with the return on line 58). Create a username/password combination by prepending the username to the MD5 encrypted form of the password. A colon is placed in between the two so that the script can parse easily when it checks usernames and passwords. Colons do not occur as output in MD5 encryption, so they are safe characters to use to delimit the two values. (Note that the script doesn't forbid colons in the usernames, which would cause user authentication to always fail if a user placed a colon in a username.) |
62 |
Attempt to write the new username/password combination to the end of the file. You know you are at the end of the file, since the while loop just parsed the entire file. If the write attempt fails, then display an error and kill the script. |
63 |
Close the file. |
64 |
Print a message letting the user know the password was added to the password file. |
65 |
End the function declaration. |
66 |
Begin the main file execution. |
67 |
Check to see if the $checkpass variable has been set. If it is set, this means that the login form was submitted to the script. |
68 |
If the $checkpass variable is set, run the check_pass() function using the user-entered username and password. |
69 |
If the check_pass() function returns true (1), notify the user that authentication succeeded. At this point, you would normally display the content that you were protecting with the user authentication. |
70–73 |
If the check_pass() function returned false, then print a message informing the user of the error. |
74–75 |
If the $checkpass variable is not set, but the $add_form variable is set, then print out the form to allow you to add new username/password combinations to the password file. |
76–77 |
If the $checkpass variable is not set and the $add_form variable is not set, but the $add variable is set, then run the add_user function using the values entered into the Add User form. |
78–79 |
If none of the above is true, then print the login form using a null value as the argument so that the login_form function does not generate a warning about use of an uninitialized variable. |
81 |
Stop parsing the page as PHP. |
82 |
Print a line to the browser that provides two links. The first is a link that sets the add_form variable so that the add_form function is called and you can add username/password combinations to the password file. The second link is used to display the login form after you have added a user. |