WWW FAQs: How do I password-protect my web pages?


2007-06-19: Many webmasters want to protect certain pages with a password. But the best way to do that depends on what you really hope to accomplish.

Do you want to require customers to create accounts, provide a valid email address, and log in before they see certain content? Then you'll need to create an account-driven website. And I'll explain how to do that.

Or do you need simple password protection for part of the site— perhaps for administrative pages that are not intended for the public? Then you won't need a full account-based system. You can use old-fashioned "basic authentication" to adequately protect your pages. And I'll explain how to do that as well.

Account-Driven Sites: Forcing Users To Log In First

Creating an account-driven website is a big subject. Fortunately, I've already written an entire article explaining exactly how to do it. So before you continue, read my article how do I add accounts to my website?

Once you have implemented accounts for your website using my PHP-based login system, Accountify, you can easily force the user to log in before they access any particular page. As my article on accounts also explains, you can simply use PHP code to determine whether a certain part of the page is visible at all:


<?php
# We must do this BEFORE any other code so it can set cookies
require 'login.php';
?>
<html>
<head>
<title>The Good Stuff</title>
</head>
<body>
<?php
# Display the login-related prompts etc
$login->prompt();
?>
<h1>The Good Stuff</h1>
<?php
  if (!$_SESSION['id']) {
?>
<h2>You must log in first.</h2>
Use the login form in the upper right corner.
<?php
  } else {
?>
<h2>PUT THE GOOD STUFF HERE!</h2>
<?php
  }
?>
</body>
</html>

Confused? You probably haven't read my article how do I add accounts to my website? yet. You should do that first.

Protecting Images

That protects the pages, but there's still a security hole here. What about the images displayed within the pages? If we use ordinary URLs in our img elements and fetch the images directly from files on the website, then it will be possible for users to copy and paste those URLs and give them out to others who do not have accounts on the system. Worse, clever thieves can and will use special tools to automatically fetch consecutively numbered image files.

If image theft is an issue for your site, you can address this problem by delivering the images with PHP code as well. Nowhere is it written that a PHP "page" must deliver HTML! It can deliver an image just as easily. Here's code to deliver an image directly from PHP after making sure the user has logged in:


<?php
# We must do this BEFORE any other code so it can set cookies
require 'login.php';
header("Content-type: image/jpeg");
if (!$_SESSION['id']) {
  readfile("/path/to/error/message.jpg");
} else {
  $img = $_GET['image'];
  if (preg_match("/^\w+\.jpg$/", $img)) {
    readfile("/path/to/images/$img");
  } else {
    readile("/path/to/error/message.jpg");
  }
}
?>

Save this file as image.php. Then, to embed an image in another page in such a way that the user cannot see it unless they have logged in, just place the image file in a folder outside your web space, set /path/to/images/ to match in the code above, and write your img element like this:


<img src="image.php?image=myimagefile.jpg">

Great! But how does it work?

First, by requiring login.php, we bring in the user's account information.

Then, by setting the Content-type to image/jpeg, we tell the web browser that the output of this PHP code is an image in the JPEG format. This is essential— without it, the assumption is always that PHP will be generating an HTML page.

Next, by checking whether $_SESSION['id'] has been set, we find out whether the user has logged in or not. If not, we send an image containing an error message instead of the image the user was hoping to see.

Finally, we fetch the name of the image file and make sure it is safe with a regular expression which allows only underscores, letters, and numbers followed by jpg. Otherwise, the user can slip ../ into the image path and access higher levels of the directory tree on your server. Never trust user input.

Once the image filename has been verified, we can safely use the readfile function to deliver the image.

Why does /path/to/images have to be a path outside your web space? If not, it will be possible to directly access it with the web browser... defeating the purpose of all our hard work! So if you absolutely can't place anything outside of your web space, at least use an unguessable folder name, and make sure that every folder has a home page or has automatic directory listings turned off.

Basic Authentication: Simple Logins For Simple Jobs

Do you simply want to protect a few pages with a password known only to you or to a few people? Then there's an easier way. It's called "HTTP Basic Authentication," and it is a built-in fuction of your web server.

As of this writing, the most common web server by a considerable margin is Apache, so I'll focus on that.

You can find a detailed tutorial on authentication, authorization, and access control on the Apache website. And I'll cover the simple case here as well.

This will work only if you have all of the following: (1) shell access via telnet or ssh, (2) access to the htpasswd program, and (3) a web server that is configured to allow this approach. If this example does not work for you, you will need to consult your web server administrator. If you are that person, you should read the much more complete tutorial on the Apache site.

Step One: to find out what your home directory is, log into your shell account via telnet or ssh. Type the command pwd and press enter. This will display your home directory path. If you cannot complete this step, you do not have shell access and you cannot follow this recipe. Consult your server administrator instead.

Step Two: assuming that your web pages are in a directory called html, your home directory is /MY/HOME/DIRECTORY and that you want to password protect a subdirectory called members that already exists, enter these commands to set up password protection for the first time. Please read these commands carefully and do not type them in literally -- you must substitute the user names that you want, as well as the path of your home directory:

htpasswd -c members.pwd firstmembername
Enter a password for 'firstmembername' when you are prompted to do so
cat > html/members/.htaccess
AuthUserFile /MY/HOME/DIRECTORY/members.pwd
AuthName Members
AuthType Basic

require valid-user

Press CONTROL-D at this point

Of course you may also use a text editor such as vi or pico to create the .htaccess file. You may even create it in notepad and upload it by FTP or SCP.

Step Three: later, when you wish to add another account, follow this much simpler recipe. Please note: you do NOT use the -c option when adding another account! -c creates a new password file and deletes your existing accounts.

htpasswd members.pwd newmembername
Enter a password for 'newmembername' when you are prompted to do so

If you receive error messages, reread this recipe very carefully. If you still receive error messages, talk to your server administrator.

Legal Note: yes, you may use sample HTML, Javascript, PHP and other code presented above in your own projects. You may not reproduce large portions of the text of the article without our express permission.

Got a LiveJournal account? Keep up with the latest articles in this FAQ by adding our syndicated feed to your friends list!