2.2. Configuration and HardeningNow that you know your installation works, make it more secure. Being brave, we start with an empty configuration file, and work our way up to a fully functional configuration. Starting with an empty configuration file is a good practice since it increases your understanding of how Apache works. Furthermore, the default configuration file is large, containing the directives for everything, including the modules you will never use. It is best to keep the configuration files nice, short, and tidy. Start the configuration file (/usr/local/apache/conf/httpd.conf) with a few general-purpose directives: # location of the web server files ServerRoot /usr/local/apache # location of the web server tree DocumentRoot /var/www/htdocs # path to the process ID (PID) file, which # stores the PID of the main Apache process PidFile /var/www/logs/httpd.pid # which port to listen at Listen 80 # do not resolve client IP addresses to names HostNameLookups Off 2.2.1. Setting Up the Server User AccountUpon installation, Apache runs as a user nobody. While this is convenient (this account normally exists on all Unix operating systems), it is a good idea to create a separate account for each different task. The idea behind this is that if attackers break into the server through the web server, they will get the privileges of the web server. The intruders will have the same priveleges as in the user account. By having a separate account for the web server, we ensure the attackers do not get anything else free. The most commonly used username for this account is httpd, and some people use apache. We will use the former. Your operating system may come pre-configured with an account for this purpose. If you like the name, use it; otherwise, delete it from the system (e.g., using the userdel tool) to avoid confusion later. To create a new account, execute the following two commands while running as root. # groupadd httpd # useradd httpd -g httpd -d /dev/null -s /sbin/nologin These commands create a group and a user account, assigning the account the home directory /dev/null and the shell /sbin/nologin (effectively disabling login for the account). Add the following two lines to the Apache configuration file httpd.conf: User httpd Group httpd 2.2.2. Setting Apache Binary File PermissionsAfter creating the new user account your first impulse might be to assign ownership over the Apache installation to it. I see that often, but do not do it. For Apache to run on port 80, it must be started by the user root. Allowing any other account to have write access to the httpd binary would give that account privileges to execute anything as root. This problem would occur, for example, if an attacker broke into the system. Working as the Apache user (httpd), he would be able to replace the httpd binary with something else and shut the web server down. The administrator, thinking the web server had crashed, would log in and attempt to start it again and would have fallen into the trap of executing a Trojan program. That is why we make sure only root has write access: # chown -R root:root /usr/local/apache # find /usr/local/apache -type d | xargs chmod 755 # find /usr/local/apache -type f | xargs chmod 644 No reason exists why anyone else other than the root user should be able to read the Apache configuration or the logs: # chmod -R go-r /usr/local/apache/conf # chmod -R go-r /usr/local/apache/logs 2.2.3. Configuring Secure DefaultsUnless told otherwise, Apache will serve any file it can access. This is probably not what most people want; a configuration error could accidentally expose vital system files to anyone caring to look. To change this, we would deny access to the complete filesystem and then allow access to the document root only by placing the following directives in the httpd.conf configuration file: <Directory /> Order Deny,Allow Deny from all </Directory> <Directory /var/www/htdocs> Order Allow,Deny Allow from all </Directory> 2.2.3.1 Options directiveThis sort of protection will not help with incorrectly or maliciously placed symbolic links that point outside the /var/www/htdocs web server root. System users could create s ymbolic links to resources they do not own. If someone creates such a link and the web server can read the resource, it will accept a request to serve the resource to the public. Symbolic link usage and other file access restrictions are controlled with the Options directive (inside a <Directory> directive). The Options directive can have one or more of the following values:
The following configuration directive will disable symbolic link usage in Apache: Options -FollowSymLinks The minus sign before the option name instructs Apache to keep the existing configuration and disable the listed option. The plus character is used to add an option to an existing configuration. If you need symbolic links consider using the Alias directive, which tells Apache to incorporate an external folder into the web server tree. It serves the same purpose but is more secure. For example, it is used in the default configuration to allow access to the Apache manual: Alias /manual/ /usr/local/apache/manual/ If you want to keep symbolic links, it is advisable to turn ownership verification on by setting the SymLinksIfOwnerMatch option. After this change, Apache will follow symbolic links if the target and the destination belong to the same user: Options -FollowSymLinks +SymLinksIfOwnerMatch Other features you do not want to allow include the ability to have scripts and server-side includes executed anywhere in the web server tree. Scripts should always be placed in special folders, where they can be monitored and controlled. Options -Includes -ExecCGI If you do not intend to use content negotiation (to have Apache choose a file to serve based on the client's language preference), you can (and should) turn all of these features off in one go: Options None
2.2.3.2 AllowOverride directiveIn addition to serving any file it can access by default, Apache also by default allows parts of configuration data to be placed under the web server tree, in files normally named .htaccess. Configuration information in such files can override the information in the httpd.conf configuration file. Though this can be useful, it slows down the server (because Apache is forced to check whether the file exists in any of the subfolders it serves) and allows anyone who controls the web server tree to have limited control of the web server. This feature is controlled with the AllowOverride directive, which, like Options, appears within the <Directory> directive specifying the directory to which the options apply. The AllowOverride directive supports the following options:
For our default configuration, we choose the None option. So, our <Directory> directives are now: <Directory /> Order Deny,Allow Deny from all Options None AllowOverride None </Directory> <Directory /var/www/htdocs> Order Allow,Deny Allow from all </Directory>
2.2.4. Enabling CGI ScriptsOnly enable CGI scripts when you need them. When you do, a good practice is to have all scripts grouped in a single folder (typically named cgi-bin). That way you will know what is executed on the server. The alternative solution is to enable script execution across the web server tree, but then it is impossible to control script execution; a developer may install a script you may not know about. To allow execution of scripts in the /var/www/cgi-bin directory, include the following <Directory> directive in the configuration file: <Directory /var/www/cgi-bin> Options ExecCGI SetHandler cgi-script </Directory> An alternative is to use the ScriptAlias directive, which has a similar effect: ScriptAlias /cgi-bin/ /var/www/cgi-bin/ There is a subtle but important difference between these two approaches. In the first approach, you are setting the configuration for a directory directly. In the second, a virtual directory is created and configured, and the original directory is still left without a configuration. In the examples above, there is no difference because the names of the two directories are the same, and the virtual directory effectively hides the real one. But if the name of the virtual directory is different (e.g., my-cgi-bin/), the real directory will remain visible under its own name and you would end up with one web site directory where files are treated like scripts (my-cgi-bin/) and with one where files are treated as files (cgi-bin/). Someone could download the source code of all scripts from the latter. Using the <Directory> directive approach is recommended when the directory with scripts is under the web server tree. In other cases, you may use ScriptAlias safely. 2.2.5. LoggingHaving a record of web server activity is of utmost importance. Logs tell you which content is popular and whether your server is underutilized, overutilized, misconfigured, or misused. This subject is so important that a complete chapter is dedicated to it. Here I will only bring your attention to two details: explaining how to configure logging and how not to lose valuable information. It is not important to understand all of the meaning of logging directives at this point. When you are ready, proceed to Chapter 8 for a full coverage. Two types of logs exist. The access log is a record of all requests sent to a particular web server or web site. To create an access log, you need two steps. First, use the LogFormat directive to define a logging format. Then, use the CustomLog directive to create an access log in that format: LogFormat "%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\ "" combined CustomLog /var/www/logs/access_log combined The error log contains a record of all system events (such as web server startup and shutdown) and a record of errors that occurred during request processing. For example, a request for a resource that does not exist generates an HTTP 404 response for the client, one entry in the access log, and one entry in the error log. Two directives are required to set up the error log, just as for the access log. The following LogLevel directive increases the logging detail from a default value of notice to info. The ErrorLog directive creates the actual log file: LogLevel info ErrorLog /var/www/logs/error_log 2.2.6. Setting Server Configuration LimitsThough you are not likely to fine-tune the server during installation, you must be aware of the existence of server limits and the way they are configured. Incorrectly configured limits make a web server an easy target for attacks (see Chapter 5). The following configuration directives all show default Apache configuration values and define how long the server will wait for a slow client: # wait up to 300 seconds for slow clients TimeOut 300 # allow connections to be reused between requests KeepAlive On # allow a maximum of 100 requests per connection MaxKeepAliveRequests 100 # wait up to 15 seconds for the next # request on an open connection KeepAliveTimeout 15 The default value for the connection timeout (300 seconds) is too high. You can safely reduce it below 60 seconds and increase your tolerance against denial of service (DoS) attacks (see Chapter 5). The following directives impose limits on various aspects of an HTTP request: # impose no limits on the request body LimitRequestBody 0 # allow up to 100 headers in a request LimitRequestFields 100 # each header may be up to 8190 bytes long LimitRequestFieldsize 8190 # the first line of the request can be # up to 8190 bytes long LimitRequestLine 8190 # limit the XML request body to 1 million bytes(Apache 2.x only) LimitXMLRequestBody 1000000 LimitXMLRequestBody is an Apache 2 directive and is used by the mod_dav module to limit the size of its command requests (which are XML-based). Seeing that the maximal size of the request body is unlimited by default (2 GB in practice), you may wish to specify a more sensible value for LimitRequestBody. You can go as low as 64 KB if you do not plan to support file uploads in the installation. The following directives control how server instances are created and destroyed in Apache 1 and sometimes in Apache 2 (as described further in the following text): # keep 5 servers ready to handle requests MinSpareServers 5 # do not keep more than 10 servers idle MaxSpareServers 10 # start with 5 servers StartServers 5 # allow a max of 150 clients at any given time MaxClients 150 # allow unlimited requests per server MaxRequestsPerChild 0 You may want to lower the maximal number of clients (MaxClients) if your server does not have enough memory to handle 150 Apache instances at one time. You should make a habit of putting a limit on the maximal number of requests served by one server instance, which is unlimited by default in Apache 1 (as indicated by the 0 MaxRequestsPerChild value) but set to 10000 in Apache 2. When a server instance reaches the limit, it will be shut down and replaced with a fresh copy. A high value such as 1000 (or even more) will not affect web server operation but will help if an Apache module has a memory leak. Interestingly, when the Keep-Alive feature (which allows many requests to be performed over a single network connection) is used, all requests performed over a single Keep-Alive connection will be counted as one for the purposes of MaxRequestsPerChild handling. Apache 2 introduces the concept of multiprocessing modules (MPMs), which are special-purpose modules that determine how request processing is organized. Only one MPM can be active at any one time. MPMs were introduced to allow processing to be optimized for each operating system individually. The Apache 1 processing model (multiple processes, no threads, each process handling one request at one time) is called prefork, and it is the default processing model in Apache 2 running on Unix platforms. On Windows, Apache always runs as a single process with multiple execution threads, and the MPM for that is known as winnt. On Unix systems running Apache 2, it is possible to use the worker MPM, which is a hybrid, as it supports many processes each with many threads. For the worker MPM, the configuration is similar to the following (refer to the documentation for the complete description): # the maximum number of processes ServerLimit 16 # how many processes to start with StartServers 2 # how many threads per process to create ThreadsPerChild 25 # minimum spare threads across all processes MinSpareThreads 25 # maximum spare threads across all processes MaxSpareThreads 75 # maximum clients at any given time MaxClients 150 Since the number of threads per process is fixed, the Apache worker MPM will change the number of active processes to obey the minimum and maximum spare threads configured. Unlike with the prefork MPM, the MaxClients directive now controls the maximum number of active threads at any given time. 2.2.7. Preventing Information LeaksBy default, Apache provides several bits of information to anyone interested. Any information obtained by attackers helps them build a better view of the system and makes it easier for them to break into the system. For example, the installation process automatically puts the email address of the user compiling Apache (or, rather, the email address it thinks is the correct email address) into the configuration file. This reveals the account to the public, which is undesirable. The following directive replaces the Apache-generated email address with a generic address: ServerAdmin webmaster@apachesecurity.net By default, the email address defined with this directive appears on server-generated pages. Since this is probably not what you want, you can turn off this feature completely via the following directive: ServerSignature Off The HTTP protocol defines a response header field Server, whose purpose is to identify the software responding to the request. By default, Apache populates this header with its name, version number, and names and version numbers of all its modules willing to identify themselves. You can see what this looks like by sending a test request to the newly installed server: $ telnet localhost 80 Trying 127.0.0.1... Connected to localhost. Escape character is '^]'. HEAD / HTTP/1.0 HTTP/1.1 200 OK Date: Fri, 19 Mar 2004 22:05:35 GMT Server: Apache/1.3.29 (Unix) Content-Location: index.html.en Vary: negotiate,accept-language,accept-charset TCN: choice Last-Modified: Fri, 04 May 2001 00:00:38 GMT ETag: "4002c7-5b0-3af1f126;405a21d7" Accept-Ranges: bytes Content-Length: 1456 Connection: close Content-Type: text/html Content-Language: en Expires: Fri, 19 Mar 2004 22:05:35 GMT This header field reveals specific and valuable information to the attacker. You can't hide it completely (this is not entirely true, as you will find in the next section), but you can tell Apache to disclose only the name of the server ("Apache"). ServerTokens ProductOnly We turned off the directory indexing feature earlier when we set the Options directive to have the value None. Having the feature off by default is a good approach. You can enable it later on a per-directory basis: <Directory /var/www/htdocs/download> Options +Indexes </Directory> Automatic directory i ndexes are dangerous because programmers frequently create folders that have no default indexes. When that happens, Apache tries to be helpful and lists the contents of the folder, often showing the names of files that are publicly available (because of an error) but should not be seen by anyone, such as the following:
To fight the problem of unintentional file disclosure, you should turn off automatic indexing (as described in the Section 2.2.3.2 section) and instruct Apache to reject all requests for files matching a series of regular expressions given below. Similar configuration code exists in the default httpd.conf file to deny access to .htaccess files (the per-directory configuration files I mentioned earlier). The following extends the regular expression to look for various file extensions that should normally not be present on the web server: <FilesMatch "(^\.ht|~$|\.bak$|\.BAK$)"> Order Allow,Deny Deny from all </FilesMatch> The FilesMatch directive only looks at the last part of the full filename (the basename), and thus, FilesMatch configuration specifications do not apply to directory names. To completely restrict access to a particular directory, for example to deny access to CVS administrative files (frequently found on web sites), use something like: <DirectoryMatch /CVS/> Order Allow,Deny Deny from all </DirectoryMatch> |