10.3. Access control by client identity

The alternative mechanism for restricting access to web pages is to demand a userid and password from the user.

HTTP "Basic Authentication"

  1. Browser sends request for a web page.

  2. Server sends back a 401 error code and specifies a realm.

  3. Browser prompts user for userid and password for the realm.

  4. User quotes userid and password.

  5. Browser repeats the initial request with an extra header quoting the userid and password.

  6. Server sends the page if the userid/password are OK.

  7. Browser sends request for another web page.

  8. Server sends back a 401 error code and specifies the same realm.

  9. Browser recovers the userid and password it has for that realm and repeats the initial request with the extra header.

  10. Server sends the page.

Of course things are different if the userid and password don't grant access to the page. There are two ways this can happen. The user and password could match but that user, now identified, might not be allowed access to the page. In this case the server sends back a 403, Forbidden, error code. Alternatively, the userid and password might not match, in which case the server sends back the 401 code again and the cycle of prompting the user repeats.

What we need to know is how to set up the server so that userids and passwords are known to the server and certain pages are flagged as requiring user authentication.

To start with, we will need some modules: auth_basic_module, authn_file_module and authz_user_module (in versions of Apache before 2.2 this functionality was all provided by auth_module). We will then specify a mechanism to identify users and finally specify policies regarding which identified users are allowed access.

So, first we need to identify users. This comes in two parts: the first involves setting up userids and passwords at the server end and the second involves telling the web server to use these for identifying users.

The userids and passwords are not the same as the login IDs. Indeed, they will often not be login IDs at all. They are maintained with a distinct file which we will need tools to manipulate. This file is traditionally called htpasswd though we have flexibility regarding its name and location. A server administrator must also decide whether to have a single password file for the whole server or one per virtual host (or even for each subtree of the virtual host he wants to restrict access to). Granting a user a userid and password noes not automatically assign that userid rights to access pages (though we can configure policy so that it does). In this example, we will work with a single userid/password file for both virtual hosts. It's a shortcoming of the Unix permissions model that we cannot specify that a file should be writable by members of either one group or another. We will use a webadmin group to control access to this file. Note that the file should not be servable by the web server.


# groupadd -r webadmin
# usermod -G www-admin,prg-admin,webadmin bob
# mkdir /etc/apache2/access
# chgrp webadmin /etc/apache2/access
# chmod g+ws /etc/apache2/access
# ls -ld /etc/apache2/access
drwxrwsr-x 2 root webadmin 4096 2007-02-23 12:16 /etc/apache2/access
# touch /etc/apache2/access/passwd
# chmod g+w /etc/apache2/access/passwd
# ls -l /etc/apache2/access/passwd
-rw-rw-r-- 1 root webadmin 0 2007-02-23 12:17 /etc/apache2/access/passwd

We make the directory writable rather than just the individual files to make life easier for programs that move files about within directories for backing up.


$ htpasswd2 -m /etc/apache2/access/passwd bob
New password: password
Re-type new password: password
Adding password for bob
$ cat /etc/apache2/access/passwd
bob:$apr1$kEDyP/..$n0DCjezTD.T.C.1s3td6..

htpasswd's -m option causes the password file to use an MD5 password encoding for the password. This is better than the traditional (and default) crypt algorithm. This makes the password much harder to reverse engineer from the file but all userid/password schemes are vulnerable to dictionary attacks and it is important that the password file not be downloaded to make this attack much harder.

Now that we have a way to identify users we need to specify policies. As with authz_host_module the restrictions on access can only be specified in a <Directory> block or in a delegated configuration file.

The simplest policy, called "valid user" is to permit access to any user who can authenticate against the web password file.

LoadModule      auth_basic_module /usr/lib/apache2/mod_auth_basic.so
LoadModule      authn_file_module /usr/lib/apache2/mod_authn_file.so
LoadModule      authz_user_module /usr/lib/apache2/mod_authz_user.so
LoadModule      authz_user_module /usr/lib/apache2/mod_authz_user.so

<Directory /srv/www/WWW/bestiary>
  AuthType      Basic
  AuthName      "Restricted area"
  AuthUserFile  /etc/apache2/access/passwd
  Require       valid-user
</Directory>

Syntax summary: implementing the "valid user" policy

<Directory /var/www/>...</Directory>

This is the standard block for restricting a set of commands to a directory tree.

The commands in this block could appear in a delegated configuration file.

AuthType Basic

This defines the protocol used for the exchange of userid and password. Every browser supports this protocol, but it does send passwords in plain text. A superior protocol, called "Digest" exists and is supported by modern browsers. See Section 10.4 for details.

AuthName "Restricted area"

This identifies the realm applying to the files in the directory tree. This string appears in the challenge for the userid and the password and is used by the browser to work out which previously given userid and password it should send without having to prompt the user again.

AuthUserFile /etc/apache2/access/passwd

This identifies the file used to contain userids and passwords. This cannot be the system /etc/passwd file!

Require valid-user

This specifies the policy. Any user validated against the password file may access the pages.

Given this setup (and a reload of the server's configuration file) we can see the effect it has on our web server. Our attempt to access the index.html page results in a challenge for userid and password.

Note that the prompt contains the phrase "Restricted area". That text comes directly from the AuthName command. If we fill in any valid userid and password from the /etc/apache2/access/passwd file we can proceed.

Next we will consider other policies. We will assume that we have created three additional web userids: tom, dick and harry.

<Directory /srv/www/WWW/bestiary>
  AuthType      Basic
  AuthName      "Restricted area"
  AuthUserFile  /etc/apache2/access/passwd
  Require       user bob tom
</Directory>

The Require user bob tom statement replaces the "valid user" policy with a "one of these users" policy.

If you plan to use certain collections of users repeatedly for access control this scheme can be taken further and groups of users can be defined. We can then specify that the validated user be one of a series of groups.

First we must define our groups. We will create a groups file this time by hand because there are no tools analogous to htpasswd to manage the files for us.

managers:   bob tom
workers:    dick harry

We also need a module that knows about group files: authz_groupfile_module (in versions of Apache before 2.2 this functionality was part of auth_module). We can then change from a user list to a group list by specifying which group file to use and which groups are permitted access.

LoadModule      authz_groupfile_module /usr/lib/apache2/mod_authz_groupfile.so

<Directory /srv/www/WWW/bestiary>
  AuthType      Basic
  AuthName      "Restricted area"
  AuthUserFile  /etc/apache2/access/passwd
  AuthGroupFile /etc/apache2/access/group
  Require       group managers
</Directory>

Syntax summary: Require

Require

The Require command specifies the policy of who is allowed access once identification is complete.

Require valid-user

Any authenticated user may have access to the pages.

Require user user1 user2 user3 ...

Only one of the listed users may have access to the pages.

Require group group1 group2 group3 ...

Any user in one or more of the listed groups may have access to the pages.

If we wanted to delegate policy regarding access control by this mechanism we must allow the override with AllowOverride AuthConfig.