Authorizing Users to |
Most server software includes its own password scheme, which is designed to work in concert with the user authentication feature built into most browsers. With this feature, a pop-up box appears that asks for the user name and password. Software already provided by the server determines if the user name and password match what is stored in a password file. If there's a match, the server allows access. If there isn't a match, the server displays an error page.
While no extra Web programs are typically required for user authentication, some Web administrators prefer their own password protection scheme. The usual approach for server-provided user authentication restricts access to specific directories at a site. But some applications require users to be able to access certain files in the directory, and not others. This is where a Web program specifically engineered to allow access to certain pages comes in handy. In this scenario, the server does not provide the page unless the proper password is given.
Obviously, you don't want someone sneaking into your server and stealing all the credit cards off your hard drive. But someone doesn't even have to do that to breach your data. Consider the following: Joe Doakes uses his credit to get access to your site. He's given a password for that access. You also allow for Joe to upgrade his account status, and since you have his credit card number there, he can view his account, change it, and so forth.
Now imagine I. M. Hacker comes to your site, sees a list of your clients in a password file, then goes about trying to luck onto a password by trying simple word lists. Suppose Joe Doakes was naive enough to use a password like "mommy," and Mr. Hacker finds it after just a few minutes of trying. Now the bad guy has access to your site in the guise of Joe Doakes, which means he can also access that account management page. Because of the lax security at your site, Mr. Hacker now has Mr. Doakes' credit card number and other personal information, and can go on a buying spree.
The above is an extreme example that has a lot of "what ifs," but it's typical of the scenarios you have to go over to determine the level of security you need for your site. You will want to consult with your Web host administrator to see what options are available to you, and if more secured access will cost you more. You can then weigh the benefits of whatever technique you use with the security requirements you have.
Note: NT Web servers, and UNIX Web servers that run other software, may also provide a method for implementing user authentication. Unfortunately, there are literally dozens of methods in current use, with many variations. It is not practical to cover all of the various user authentication methods that are available. The UNIX approach shown here is employed in the vast majority of Web hosts around the world, and is the one you are most likely to encounter. If your Web host uses a different method, contact the Web host administrator for details on implementing user authentication.User authentication under UNIX centers around two files: the access file, and the password file.
Below are more detail discussions of the .htaccess and .htpasswd files.
AuthUserFile /disk02/.htpasswd AuthGroupFile /dev/null AuthName Name goes_here AuthType Basic <Limit GET> require valid-user </Limit>
Note: the .htaccess access file is actually used for many more functions than user authentication. For example, it is also used to change the visual appearance of directory listings and modify the contents of error messages returned by the Web server. For our application though, we will limit the discussion of the .htaccess access file to user authentication only.Following is a line-by-line description of the contents of the .htaccess file.
Option | What it does |
all | all hosts are allowed/disallowed access |
domain name | Domain name of a host; e.g. mysite.com |
IP address | IP address of a host; e.g. 255.255.255.255 |
allow from .machine.mysite.comAll hosts in the specified domain are allowed access.
or
deny from .mysite.comAll hosts in the specified domain are allowed denied.
You can also use the order directive, to specifically control the order
in which allow and deny directives are evaluated.
Option | What it does |
deny,allow | Deny directives are evaluated before the allow directives. |
allow,deny | Allow directives are evaluated before the deny directives. |
mutual-failure | Only hosts that appear on allow list, but not deny list, are granted access. |
<Limit GET> order allow,deny allow from mysite.com deny from yoursite.com anothersite.com require valid-user </Limit>This example first checks for all allowed domains, in this case mysite.com. Then it checks for all disallowed domains, in this case yoursite.com as well as anothersite.com. Note that there cannot be a space between the directives. That is, "allow,deny" is acceptable, but "allow, deny" is not.
See "Creating a .htpasswd File," later in this chapter, for details on creating the user names and passwords in a .htaccess file.
The format of the AuthUserFile is:
username1:password1 username2:password2 username3:password3and so forth. Each username/password pair is on a separate line, and each username is separated by its password with a colon. The actual password is encrypted; that is; the plain-text password does not show up on the AuthUserfile, but rather the encrypted version. This version uses an encryption algorithm that is extremely difficult to crack, so anyone getting a copy of the AuthUserFile will not necessarily be able to glean any useful passwords from it.
The Apache documentation for the AuthUserFile stipulates that the behavior is "undefined" if the server encounters multiple instances of the same user name. Usually, the server will pick the first user name it finds, but in some cases a server error can result. Therefore, it is important to ensure that user names occur only once in a AuthUserFile.
Note that the AuthUserFile is a "flat" text database, and is therefore not very efficient if there are lots of username/passwords. You can reliably use the AuthUserFile if you have upwards of 500 to 750 username/passwords. With more entries in the file, consider using the DBM authorization system supported by most UNIX-based Web servers. However, not all Web hosts support the DBM authorization module. Check with your Web host administrator if you need to provide access to more than 500 or 750 user names.
It is very important that AuthUserFile is not placed in a directory that is servable by the Web server. See the section "Using an Out-of-Reach Directory," later in this chapter. And especially, DO NOT place the file in the same directory that is protected. Otherwise, users will be able to fetch the AuthUserFile, and at the very least get a listing of your authorized users.
Note: It is considered very difficult to crack UNIX passwords. Even the UNIX operating system and the Web server don't actually "decrypt" the password. Rather, the server merely matches the already crypted password with a crypted version of the test password. If they match, then the server grants access. This is how a hacker's program works. If they stumble onto the .htaccess file, it will point to the .htpasswd file. If this file is in a servable directory, they can retrieve it, and get the username:password pairs. Once they have that file, they can do a dictionary lookup to see if there are any matches to the crypted passwords. Odds are, they'll find one.
mygroup: fred ellen joe
Note: The AuthGroupFile is seldom used for authorizing users in a Web commerce site. Rather, use AuthUserFile.
/web/mysite/htdocs/in the hierarchy of paths on the computer. Files and other subdirectories located under the ../htdocs path are accessible to persons visiting your site. However, unless the following paths are also indicated as being document roots, files in these are not servable, and therefore not viewable to anyone visiting your site
/web/ /web/mysite/ /web/mysite/anotherdir/The out of reach directory concept is particularly important with user authentication, because the .htpasswd password file can be placed there, which "hides" the file from anyone visiting your site. However, files stored in the out of reach directory are still accessible by the operating system, and by CGI programs. For maximum security, set up your sire so that the .htpasswd password file is in a directory outside the document root. Assuming
/web/mysite/htdocs/is the document root for your Web site, then
/web/myseite/oodis used as an "out of reach" directory for sensitive files.
Check with your site administrator to see if they can set you up with a directory structure to allow for out-of-reach directories. Some Web hosts don't have a provision for this, and it is best to determine the specifics directly from the Web host administrator. If your Web host can set up a special directory for you, they can suggest how to proceed.
Note that there is nothing terribly wrong with having the htpasswd file out in the open (a servable directory). But doing so does mean you are an easier target for hackers. You need to decide what you're protecting against, and design the system around that. Depending on the Web server, you can put htaccess in the /ood directory, but it hardly makes any difference. Non members probably won't know about the existence of the directory anyway. And it's validated users who can become your worst hackers. They have access to your htaccess file. Could this be a problem for you? Only you can decide.
.htpasswd-dir1 .htpasswd-dir2 .htpasswd-dir3or whatever. You will find site maintenance easier if all the password files are in a single, safe directory.
htpasswd .htpasswd fredthen at the password prompt, type:
doggieThe htpasswd file will ask you to repeat the password, to be sure it is entered correctly.
Note: If the .htpasswd file does not already exist, use the -c switch to create a new file:
htpasswd -c .htpasswd fredIf the .htaccess file is not in the same directory as the htpassswd program, be sure to specify the absolute path to the file. For example:
htpasswd /disk/mysite.com/ood/.htpasswd fred
Creating a blink directory or file is simple: merely add the directory and file in the usual manner. Be sure that there are no links to the directory/file, and that your server does not display a directory index that might otherwise display the hidden directory and file.
On the other hand, most of us don't work with truly sensitive stuff. We merely want to provide a barrier to restrict casual access by the general public. One low-tech approach is to merely remove any external links to your sensitive page (see "Password Protection Using 'Blind' Directories or Filenames," above). That way, users must explicitly type the path and name of the document. This method works best if the document is in a directory that contains an "index" or main home page. That way, if a user specifies just the path, they get the index document, and not the directory of files in that path.
The problem with this method is that once you give out the filename people can continue to access it. You may wish to limit the accesses to your restricted pages in some way -- for example, allow access on one day, but not on another. For this application JavaScript can help. With JavaScript, you can create a basic, no-frills enciphering program that converts a plain-text filename to an enciphered filename.
A numeric key can be used to increase the variability of the enciphering. The key has 63 possible values. Each of the keys results in a different enciphered filename from the same "plain-text" filename. You might use values 1 through 31, for example, as keys for each day of the month. This allows you to restrict access to your pages on specific days.
The encoding system used in the examples in this section is extremely simple, and can be broken in a matter of minutes by a trained cryptographer. However, to the average user the encoding scheme is not immediately obvious; there are no "secret words" or numbers stored in the script that a user can view.
Note: For the inquisitive, the script uses a simple cipher technique known as XORing, where the numeric value of each character of the plain-text password is mixed with a numeric key value. The result is similar to the old-fashioned "decoder ring" they used to give away in cereal boxes, where each letter is substituted for another. The benefit is that it's easy to change the key against which the letter values are matched, and that the key is not part of the message or script.A reverse process can be used to decode the cipher back to its plain-text filename. In fact, the exact same JavaScript program is used as both the encoder and as the decoder. Use the cipher text as the password, and provide the same key value used for encoding (remember: the encrypted text alone isn't enough -- you need the key value!).
<HTML> <HEAD> <TITLE>Password Encyphering test</TITLE> <SCRIPT LANGUAGE="JavaScript"> function testEncode(form) { var Ret = encode (form.inputbox1.value, form.inputbox2.value) form.inputbox3.value = Ret } function encode (OrigString, CipherVal) { Ref="0123456789abcdefghijklmnopqrstuvwxyz._~" Ref=Ref+"ABCDEFGHIJKLMNOPQRSTUVWXYZ" CipherVal = parseInt(CipherVal) var Temp="" for (Count=0; Count < OrigString.length; Count++) { var TempChar = OrigString.substring (Count, Count+1) var Conv = cton(TempChar) var Cipher=Conv^CipherVal Cipher=ntoc(Cipher) Temp += Cipher } return (Temp) } function cton (Char) { return (Ref.indexOf(Char)); } function ntoc (Val) { return (Ref.substring(Val, Val+1)) } </SCRIPT> </HEAD> <BODY> <FORM NAME="testform"> Plain text: <BR> <INPUT TYPE="text" NAME="inputbox1" VALUE=""><P> Key value:<BR> <INPUT TYPE="text" NAME="inputbox2" VALUE=""><P> Cipher:<BR> <INPUT TYPE="text" NAME="inputbox3" VALUE=""><P> <INPUT TYPE="button" NAME="button" Value="Encode" onClick="testEncode(this.form)"><BR> </FORM> </BODY> </HTML>To use, enter a word to encrypt. The script is set up to use only lower-case values, underscores, and periods (like Web filenames). Avoid using upper-case letters, and do not use any characters not allowed in Web filenames. You must also enter a key value, from 1 to 63. Leaving this entry blank or using a 0 will result in the same cipher text as the plain text. Click the Encode button to view the enciphered result. For example, suppose you specify the_beatles as the plain-text, and 4 as the key. The resulting cipher text is plaxfaephao. Therefore, the filename you will use with this combination is plaxfaephao. It's up to you if you wish to add an htm or html extension (some servers are a bit picky when it comes to files without extensions).
If your server limits filenames to eight characters, the plain-text filename should likewise be limited to eight characters. Example: If you specify beatles as the plain-text, and 12 as the key, you get 726hp2g.
The password.html file demonstrates a JavaScript program showing the basic principle of allowing access to the restricted page. The program allows the user to enter a password. Clicking the Submit button decodes the password, and links to that page. Note that if the user selects the wrong password, an incorrect decipher string is generated, and the browser attempts to link to a file that does not exist. An error message results. This error message provided by the server cannot be avoided.
The key value gives you many more password combinations. The password.html file uses the current month as the key value. This allows the user to access the page for one month only. The next month the key value changes, and therefore the same password yields a different enciphered result. This system is particularly useful if you cannot update the restricted files on a regular basis. The files "self-expire" according to the current month.
<HTML> <HEAD> <TITLE>JavaScript Password File</TITLE> <SCRIPT LANGUAGE="JavaScript"> function testEncode(form) { var dater = new Date(); Month = dater.getMonth()+1; dater = null; var Ret = encode (form.inputbox1.value, Month) location = Ret + ".html" } function encode (OrigString, CipherVal) { Ref="0123456789abcdefghijklmnopqrstuvwxyz._~" Ref=Ref+"ABCDEFGHIJKLMNOPQRSTUVWXYZ" CipherVal = parseInt(CipherVal) var Temp="" for (Count=0; Count < OrigString.length; Count++) { var TempChar = OrigString.substring (Count, Count+1) var Conv = cton(TempChar) var Cipher=Conv^CipherVal Cipher=ntoc(Cipher) Temp += Cipher } return (Temp) } function cton (Char) { return (Ref.indexOf(Char)); } function ntoc (Val) { return (Ref.substring(Val, Val+1)) } </SCRIPT> </HEAD> <BODY> <FORM NAME="testform" onSubmit=false;> Please enter your password: <BR> <INPUT TYPE="text" NAME="inputbox1" VALUE=""><P> <INPUT TYPE="button" NAME="button" Value="Submit" onClick="testEncode(this.form)"><BR> <INPUT TYPE="hidden" NAME="hidden" VALUE=""><P> </FORM> </BODY> </HTML>
(Abridged from Web Commerce Cookbook, published by Wiley Computer Publishing and written by Gordon McComb. Copyright © 1997, Gordon McComb. All Rights Reserved. Please see http://gmccomb.com/commerce/ for more details on this book.)