Certain parts of your website will almost certainly need to be protected from being viewed by the general public; access control allows you to restrict these sections to authorized users either by hostname/IP address or by password authentication. We'll take a look at both of these methods. Access Control by AddressLet's say you want to restrict a certain file, directory, or set of files and directories to a short list of fixed IP addresses or hostnames. You can do this at the global (httpd.conf) level if you have a permanent area of your site (such as a set of administration scripts) that you want to keep secure; or more efficiently for regular users or for temporary private resources, you can use an .htaccess file in any directory at or above the level of the items you want to protect. Begin by making sure that .htaccess overrides are enabled in the part of the site that you want to protect. As you saw earlier, you can use AllowOverride Limit, at the /usr/local/www/data level or further down in the directory structure with another <Directory> container, to enable the use of the access-control directives Allow, Deny, and Order. Because Apache directives are all read in at the same time, you need to specify the order in which to read the Allow and Deny directives; otherwise, they'll override each other in ways you don't intend. To close off a directory to everyone but certain specified addresses or hostnames, put an .htaccess file in that directory with the following contents: Order deny,allow Deny from all Allow from 64.41.131.102 Allow from stripes.example.com Allow from nowhere.com Allow from 10.5.100 Allow from 10.67.22.211/255.255.255.0 As you can see, a number of different address formats are allowed; these are just matching rules, where if the connecting host matches any of these hostname or network patterns, it will be allowed access. Similarly, you can have the directory open to everybody except certain hosts: Order allow,deny Allow from all Deny from l33t.hacker.com Deny from 192.168 You must place these commands inside a <Directory> or <Location> block if you're doing this in httpd.conf; you can also use <Files> or <FilesMatch> to list certain files, wherever in the system they might be, which should be subject to the access control: <Files "*.jpg"> Order deny,allow Deny from all Allow from 64.41.131.102 </Files> FilesMatch can be used to specify filenames matching regular expressions, as in this example, which would match all .gif, .jpg, .jpeg, and .png files: <FilesMatch "\.(gif|jpe?g|png)$"> Order deny,allow Deny from all Allow from 64.41.131.102 </FilesMatch> Note, however, that <Directory> and <Location> are not available in .htaccess files. The <Limit> block, despite its name, is not required for the preceding access controls to work. Rather, its purpose is to list certain access methods to which the access controls should apply. For instance, you might choose to limit only GET and POST but leave all other methods unrestricted. Likewise, you could use <LimitExcept> to specify the only methods to which the access controls should not apply. You saw an example of this earlier, in the commented-out block controlling user directories within /home. Caution Access restriction by address is prone to failureIP addresses can be forged by a sufficiently determined attacker. What's more, a failure to detect a forged address is transparent and all but untraceable; if you use passwords instead, there's a dialogue that can be logged with much greater detail. Opt for password-based access control rather than address-based whenever possible. Access Control by PasswordSometimes, it's not practical or desirable to limit access by address. A privileged portion of your site might be made available to registered users who have paid a fee, for example, and these users might not come from predictable IP addresses. This is where it's much more sensible to use password authentication for access control. Apache stores its own username and password databases, distinct from the systemwide one in /etc/master.passwd and its relatives. This is for security purposes, and it's an extremely bad idea to try to merge the two authentication schemes together, so don't even think about it. The FreeBSD password database is for authenticating shell users who actually have an account on the system, whereas the Apache authentication databases control access to certain parts of your website for web surfers. You might have a situation in which these two areas of functionality overlap (such as an intranet server for a company), but don't be tempted to try to serve both from the same database. If you succeed, it will be a study in insecurity and irresponsible system administration techniques. This practice is explicitly discouraged by the Apache Group and will earn you a great deal more scorn than respect from your peers. It's not worth it. To password-protect a portion of your website, you'll need to use an .htaccess file (or equivalent configuration in httpd.conf)including the appropriate use of <Directory>, <Location>, and <Files> blocksjust as with per-address access control. The contents of this file or configuration block are considerably different, though, involving an entirely different module in Apache to do the access control. A simple example of such a block follows: AuthType Basic AuthName "Restricted Area" AuthUserFile /usr/local/www/.htpasswd Require valid-user This example includes the minimum number of directives needed to make password-protection work; each of these directives, or their alternatives, is required. These directives are a little confusing, so let's look at them individually:
Adding UsersAfter you have the access control block set up and apachectl configtest reports that the configuration is okay, it's time to add users. First, let's see how to make a plain-text user database, the kind for which you'd use an AuthUserFile directive. The command to use is htpasswd, which in FreeBSD is in /usr/local/bin and therefore part of your path: # htpasswd -c /usr/local/www/.htpasswd frank Note The -c option is only necessary the first time you run htpasswd; this tells the program to create the file (because it doesn't exist yet), and you can omit the -c option every subsequent time. You will then be prompted for the user's password, which you must enter twice (as usual). To do this in a script, you can use the -b option and specify the password as another argument: # htpasswd -bm /usr/local/www/.htpasswd joe Pr1d3L4ndz The other option used here is -m, which causes htpasswd to use an MD5-based encryption algorithm rather than the system's crypt() routine; this may provide you with better security. Other options can be found in the man apachectl page. Caution The user database file can be placed anywhere in the system that the web server user (nobody) can read, and it can be named anything you want, although having the name begin with .htpasswd (for example, .htpasswd-restrictedarea) is traditional and protects it from being viewed in directory listings, either on the shell or via the web server. However, it's a very bad idea to put the file anywhere where it can be accessed by a user with a web browser! To wit, don't put it anywhere inside /usr/local/www/data or in any user's public_html directory; instead, use web-inaccessible locations such as /usr/local/www or directly inside a user's home directory. You don't want people downloading your user database and cracking it! Plain-text user databases work great for small lists of users; however, once you start working with dozens or hundreds of users, the time it takes to look up a user's password makes the authentication process cumbersome or even nonfunctional. The solution to this is to use a true database file, either in db or dbm form, and to use the appropriate AuthDBUserFile or AuthDBMUserFile directive in your access control block. The support program that allows you to work with the database files is /usr/local/bin/dbmmanage: # dbmmanage /usr/local/www/.htpwddb adduser frank New password: Re-type new password: User frank added with password encrypted to NtMDxy6jwyW7A using crypt This example will create a db-style database file called /usr/local/www/.htpwddb, which you can then specify with the AuthDBUserFile directive. The dbm equivalent can also be used if necessary, but you shouldn't have to. See man dbmmanage for further details on the user management commands you can use. Adding GroupsListing groups in a plain-text file (AuthGroupFile) is quite easy. Each line contains the group name, a colon, and then the list of users in the group: mygroup: frank joe alice The group file should probably be kept in the same place as the user file, and with a similar name; however, because the group file doesn't contain any passwords, security considerations aren't quite as critical. Just make sure that only root has the ability to write to the file! Group files can be managed with dbmmanage as well, but this practice is usually unnecessary because (as with /etc/group) there are seldom anywhere near as many groups as there are users. You can use AuthGroupFile in conjunction with AuthDBUserFile if you want to mix formats; this is probably the simplest way to maintain a large user list along with a small group list. Access Control by Address and PasswordIt's possible to require that a user satisfy both host address restrictions and password authentication before access control allows access to a restricted area. This is done with the Satisfy directive: Allow from 64.41.131.102 Require valid-user Satisfy all Satisfy all is the default setting if both Allow and Require directives are present; this behavior means that the incoming user must meet the Allow requirements and supply a valid username and password before access will be granted. You can also use Satisfy any to tell the server that the user must supply a valid password or be coming from an allowed address; this is useful if you want users at certain locations to have unconditional access to an area, but require authentication from all other users (for instance, if you're providing a service that you must administer through the same interface as every other user, but you don't want to be forever entering passwords for access). |