7.3. Access Control in ApacheOut of the box, Apache supports the Basic and Digest authentication protocols with a choice of plaintext or DBM files (documented in a later section) as backends. (Apache 2 also includes the mod_auth_ldap module, but it is considered experimental.) The way authentication is internally handled in Apache has changed dramatically in the 2.1 branch. (In the Apache 2 branch, odd-number releases are development versions. See http://cvs.apache.org/viewcvs.cgi/httpd-2.0/VERSIONING?view=markup for more information on new Apache versioning rules.) Many improvements are being made with little impact to the end users. For more information, take a look at the web site of the 2.1 Authentication Project at http://mod-auth.sourceforge.net. Outside Apache, many third-party authentication modules enable authentication against LDAP, Kerberos, various database servers, and every other system known to man. If you have a special need, the Apache module repository at http://modules.apache.org is the first place to look. 7.3.1. Basic Authentication Using Plaintext FilesThe easiest way to add authentication to Apache configuration is to use mod_auth , which is compiled in by default and provides Basic authentication using plaintext password files as authentication source. You need to create a password file using the htpasswd utility (in the Apache /bin folder after installation). You can keep it anywhere you want but ensure it is out of reach of other system users. I tend to keep the password file at the same place where I keep the Apache configuration so it is easier to find: # htpasswd -c /usr/local/apache/conf/auth.users ivanr New password: ****** Re-type new password: ****** Adding password for user ivanr This utility expects a path to a password file as its first parameter and the username as its second. The first invocation requires the -c switch, which instructs the utility to create a new password file if it does not exist. A look into the newly created file reveals a very simple structure: # cat /usr/local/apache/conf/auth.users ivanr:EbsMlzzsDXiFg You need the htpasswd utility to encrypt the passwords since storing passwords in plaintext is a bad idea. For all other operations, you can use your favorite text editor. In fact, you must use the text editor because htpasswd provides no features to rename accounts, and most versions do not support deletion of user accounts. (The Apache 2 version of the httpasswd utility does allow you to delete a user account with the -D switch.) To password-protect a folder, add the following to your Apache configuration, replacing the folder, realm, and user file specifications with values relevant for your situation: <Directory /var/www/htdocs/review/> # Choose authentication protocol AuthType Basic # Define the security realm AuthName "Book Review" # Location of the user password file AuthUserFile /usr/local/apache/conf/auth.users # Valid users can access this folder and no one else Require valid-user </Directory> After you restart Apache, access to the folder will require valid login credentials. 7.3.1.1 Working with groupsUsing one password file per security realm may work fine in simpler cases but does not work well when users are allowed access to some realms but not the others. Changing passwords for such users would require changes to all password files they belong to. A better approach is to have only one password file. The Require directive allows only named users to be allowed access: # Only the book reviewers can access this folder Require user reviewer1 reviewer2 ivanr But this method can get out of hand as the number of users and realms rises. A better solution is to use group membership as the basis for authentication. Create a group file, such as /usr/local/apache/conf/auth.groups, containing a group definition such as the following: reviewers: reviewer1 reviewer2 ivanr Then change the configuration to reference the file and require membership in the group reviewers in order to allow access: <Directory /var/www/htdocs/review/> AuthType Basic AuthName "Book Review" AuthUserFile /usr/local/apache/conf/auth.users # Location of the group membership file AuthGroupFile /usr/local/apache/conf/auth.groups # Only the book reviewers can access this folder Require group reviewers </Directory> 7.3.2. Basic Authentication Using DBM FilesLooking up user accounts in plaintext files can be slow, especially when the number of users grows over a couple of hundred. The server must open and read the file sequentially until it finds a matching username and must repeat this process on every request. The mod_auth_dbm module also performs Basic authentication, but it uses efficient DBM files to store user account data. DBM files are simple databases, and they allow usernames to be indexed, enabling quick access to the required information. Since mod_auth_dbm is not compiled in by default, you will have to recompile Apache to use it. Using mod_auth_dbm directives instead of mod_auth ones in the previous example gives the following: <Directory /var/www/htdocs/review/> AuthType Basic AuthName "Book Review" AuthDBMUserFile /usr/local/apache/conf/auth.users.dat # Location of the group membership file. Yes, # it points to the same file as the password file. AuthDBMGroupFile /usr/local/apache/conf/auth.users.dat # Only the book reviewers can access this folder Require group reviewers </Directory> The directive names are almost the same. I added the .dat extension to the password and group file to avoid confusion. Since DBM files cannot be edited directly, you will need to use the dbmmanage utility to manage the password and group files. (The file will be created automatically if it does not exist.) The following adds a user ivanr, member of the group reviewers, to the file auth.users.dat. The dash after the username tells the utility to prompt for the password. # dbmmanage /usr/local/apache/conf/auth.users.dat adduser ivanr - reviewers New password: ****** Re-type new password: ****** User ivanr added with password encrypted to 9yWQZ0991uFnc:reviewers using crypt 7.3.3. Digest AuthenticationThe use of Digest authentication requires the mod_auth_digest module to be compiled into Apache. From an Apache administrator's point of view Digest authentication is not at all difficult to use. The main difference with Basic authentication is the use of a new directive, AuthDigestDomain. (There are many other directives, but they control the behavior of the Digest authentication implementation.) This directive accepts a list of URLs that belong to the same protection space. <Directory /var/www/htdocs/review/> AuthType Digest AuthName "Book Review" AuthDigestDomain /review/ AuthDigestFile /usr/local/apache/conf/auth.users.digest Require valid-user </Directory> The other difference is that a separate utility, htdigest, must be used to manage the password database. As mentioned earlier, Digest authentication forces you to use one password database per protection space. Without a single user database for the whole server, the AuthDigestGroupFile directive is much less useful. (You can have user groups, but you can only use them within one realm, which may happen, but only rarely.) Here is an example of using htdigest to create the password database and add a user: # htdigest -c /usr/local/apache/conf/auth.users.digest "Book Review" ivanr Adding password for ivanr in realm Book Review. New password: ****** Re-type new password: ****** 7.3.4. Certificate-Based Access ControlThe combination of any of the authentication methods covered so far and SSL encryption provides a solid authentication layer for many applications. However, that is still one-factor authentication. A common choice when two-factor authentication is needed is to use private client certificates. To authenticate against such a system, you must know a password (the client certificate passphrase, a Type 1 factor) and possess the certificate (a Type 2 factor). Chapter 4 discusses cryptography, SSL, and client certificates. Here, I bring a couple of authentication-related points to your attention. Only two directives are needed to start asking clients to present their private certificates provided everything else SSL-related has been configured: SSLVerifyClient require SSLVerifyDepth 1 This and the use of the SSLRequireSSL directive to enforce SSL-only access for a host or a directory will ensure only strong authentication takes place. The SSLRequire directive allows fine access control using arbitrarily complex boolean expressions and any of the Apache environment variables. The following (added to a directory context somewhere) will limit access to a web site only to customer services staff and only during business hours: SSLRequire ( %{SSL_CLIENT_S_DN_OU} eq "Customer Services" ) and \ ( %{TIME_WDAY} >= 1 and %{TIME_WDAY} <= 5 ) and \ ( %{TIME_HOUR} >= 8 and %{TIME_HOUR} <= 19 )
The full reference for the SSLRequire directive is available in the Apache documentation: 7.3.5. Network Access ControlNetwork access control is performed with the help of the mod_access module. Directives Allow and Deny are used to allow or deny access to a directory. Each directive takes a hostname, an IP address, or a fragment of either of the two. (Fragments will be taken to refer to many addresses.) A third directive, Order, determines the order in which allow and deny actions are evaluated. This may sound confusing and it is (always has been to me), so let us see how it works in practice. To allow access to a directory from the internal network only (assuming the network uses the 192.168.254.x network range): <Directory /var/www/htdocs/review/> Order Deny,Allow Deny from all Allow from 192.168.254. </Directory> You are not required to use IP addresses for network access control. The following identification formats are allowed:
Do the following to let anyone but the users from the internal network access the directory: <Directory /var/www/htdocs/review/> Order Allow,Deny Allow from all Deny from 192.168.254. </Directory> The addresses in Allow and Deny can overlap. This feature can be used to create exceptions for an IP address or an IP address range, as in the following example, where access is allowed to users from the internal network but is explicitly forbidden to the user whose workstation uses the IP address 192.168.254.125: <Directory /var/www/htdocs/review/> Order Allow,Deny Allow from 192.168.254. Deny from 192.168.254.125 # Access will be implicitly denied to requests # that have not been explicitly allowed. </Directory> With Order set to Allow,Deny, access is denied by default; with Deny,Allow, access is allowed by default. To make it easier to configure network access control properly, you may want to do the following:
7.3.5.1 Using environment variablesAllow and Deny support a special syntax that can be used to allow or deny access based not on the request IP address but on the information available in the request itself or on the contents of an environment variable. If you have mod_setenvif installed (and you probably do since it is there by default), you can use the SetEnvIf directive to inspect incoming requests and set an environment variable if certain conditions are met. In the following example, I use SetEnvIf to set an environment variable whenever the request uses GET or POST. Later, such requests are allowed via the Allow directive: # Set the valid_method environment variable if # the request method is either GET or POST SetEnvIf Request_Method "^(GET|POST)$" valid_method=1 # Then only allow requests that have this variable set <Directory /var/www/htdocs/review/> Order Deny,Allow Deny from all Allow from env=valid_method </Directory> 7.3.6. Proxy Access ControlRestricting access to a proxy server is very important if you are running a forward proxy, i.e., when a proxy is used to access other web servers on the Internet. A warning about this fact appears at the beginning of the mod_proxy reference documentation (http://httpd.apache.org/docs-2.0/mod/mod_proxy.html). Failure to properly secure a proxy will quickly result in spammers abusing the server to send email. Others will use your proxy to hide their tracks as they perform attacks against other servers. In Apache 1, proxy access control is done through a specially named directory (proxy:), using network access control (as discussed in the Section 7.3.5): # Allow forward proxy requests ProxyRequests On # Allow access to the proxy only from # the internal network <Directory proxy:*> Order Deny,Allow Deny from all Allow from 192.168.254. </Directory> In Apache 2, the equivalent <Proxy> directive is used. (Apache 2 also provides the <ProxyMatch> directive, which allows the supplied URL to be an arbitrary regular expression.) # Allow forward proxy requests ProxyRequests On # Allow access to the proxy only from # the internal network <Proxy *> Order Deny,Allow Deny from all Allow from 192.168.254. </Proxy> Proxying SSL requests requires use of a special CONNECT method, which is designed to allow arbitrary TCP/IP connection tunneling. (See Chapter 11 for examples.) Apache will allow connection tunneling to target only ports 443 (SSL) and 563 (SNEWS) by default. You should not allow other ports to be used (using the AllowCONNECT directive) since that would allow forward proxy users to connect to other services through the proxy. One consequence of using a proxy server is transfer of trust. Instead of users on the internal network, the target server (or application) is seeing the proxy as the party initiating communication. Because of this, the target may give more access to its services than it would normally do. One common example of this problem is using a forward proxy server to send email. Assuming an email server is running on the same machine as the proxy server, this is how a spammer would trick the proxy into sending email: POST http://localhost:25/ HTTP/1.0 Content-Length: 120 MAIL FROM: aspammer RCPT TO: ivanr@webkreator.com DATA Subject: Please have some of our spam Spam, spam, spam... . QUIT This works because SMTP servers are error tolerant. When receiving the above request, the proxy opens a connection to port 25 on the same machine (that is, to the SMTP server) and forwards the request to that server. The SMTP server ignores errors incurred by the HTTP request line and the header that follows and processes the request body normally. Since the body contains a valid SMTP communication, an email message is created and accepted. Unlike for the CONNECT method, Apache does not offer directives to control target ports for normal forward proxy requests. However, Apache Cookbook (Recipe 10.2) provides a solution for the proxy-sending-email problem in the form of a couple of mod_rewrite rules: <Proxy *> RewriteEngine On # Do not allow proxy requests to target port 25 (SMTP) RewriteRule "^proxy:[a-z]*://[^/]*:25(/|$)" "-" [F,NC,L] </Proxy> 7.3.6.1 Reverse proxiesThe use of a reverse proxy does not require access control, but it is essential to turn the forward proxy off in the Apache configuration: # We are running a reverse proxy only, do not # allow forward proxy requests ProxyRequests Off 7.3.7. Final Access Control NotesI will mention more Apache directives related to access control. Prior to presenting that information, I would like to point out one more thing: many modules other than the ones described in this chapter can also be used to perform access control, even if that isn't their primary purpose. I have used one such module, mod_rewrite, many times in this book to perform things that would be impossible otherwise. Some modules are designed to perform advanced access control. This is the case with mod_dosevasive (mentioned in Chapter 5) and mod_security (described in detail in Chapter 12). 7.3.7.1 Limiting request methodsThe <Limit> and <LimitExcept> directives are designed to perform access control based on the method used in the request. Each method has a different meaning in HTTP. Performing access control based on the request method is useful for restricting usage of some methods capable of making changes to the resources stored on the server. (Such methods include PUT, DELETE, and most of the WebDAV methods.) The possible request methods are defined in the HTTP and the WebDAV specifications. Here are descriptions and access control guidance for some of them:
The <Limit> directive allows access control to be performed for known request methods. It is used in the same way as the <Directory> directive is to protect directories. The following example allows only authenticated users to make changes on the server using the PUT and DELETE methods: <Limit PUT DELETE> AuthType Basic AuthName "Content Editors Only" AuthUserFile /usr/local/apache/conf/auth.users Require valid-user </Limit> Since the <Limit> directive only works for named request methods, it cannot be used to defend against unknown request methods. This is where the <LimitExcept> directive comes in handy. It does the opposite and only allows anonymous access to requests using the listed methods, forcing authentication for others. The following example performs essentially the equivalent functionality as the previous example but forces authentication for all methods except GET, HEAD, and POST: <LimitExcept GET HEAD POST> AuthType Basic AuthName "Content Editors Only" AuthUserFile /usr/local/apache/conf/auth.users Require valid-user </LimitExcept> 7.3.7.2 Combining authentication with network access controlAuthentication-based and network-based access control can be combined with help from the Satisfy configuration directive. This directive can have two values:
This feature is typically used to relax access control in some specific cases. For example, a frequent requirement is to allow internal users access to a resource without providing passwords, but to require authentication for requests coming in from outside the organization. This is what the following example does: <Directory /var/www/htdocs> # Network access control Order Deny,Allow Deny from all Allow from 192.168.254. # Authentication AuthType Basic AuthName "Content Editors Only" AuthUserFile /usr/local/apache/conf/auth.users Require valid-user # Allow access if either of the two # requirements above are satisfied Satisfy Any </Directory> 7.3.7.3 Combining multiple authentication modulesThough most authentication examples only show one authentication module in use at a time, you can configure multiple modules to require authentication for the same resource. This is when the order in which the modules are loaded becomes important. The first authentication module initialized will be the first to verify the user's credentials. With the default configuration in place, the first module will also be the last. However, some (possibly all) authentication modules support an option to allow subsequent authentication modules to attempt to authenticate the user. Authentication delegation happens if the first module processing the request is unable to authenticate the user. In practice, this occurs if the user is unknown to the module. If the username used for the request is known but the password is incorrect, delegation will not happen. Each module uses a directive with a different name for this option, but the convention is to have the names end in "Authoritative." For example, the AuthAuthoritative directive configures mod_auth, and the AuthDBMAuthoritative directive configures mod_auth_dbm. |