Securing Apache, Part 11: Logs, et al.

Tightened Security

In this final part of the series, we will discover how to strengthen security in Apache by logging and other miscellaneous ways.

Configuring a system to be secure is indeed a key task, but it is also important to know that the configuration is working properly — and the only way to do so is through log analysis. Sensible logging helps detect performance problems well before they become apparent to users, and provides evidence of potential security problems. Maintaining logs is also useful for traffic analysis.

Apache can produce many types of logs, the two essential ones being the access log, where all requests are noted, and the error log, which is designed to log various informational and debug messages, plus every exceptional event that occurs. You, as Web master, have a limited amount of control over the logging of error conditions, but a great deal of control over the format and amount of information logged about request processing (access log). The server may log activity information about a request in multiple formats in many log files, but it will only record a single copy of an error message.

One aspect of access logs that you should be aware of is that the log entry is formatted and written after the request has been completely processed. This means that the interval between the time a request begins and when it finishes may be long enough to make a difference. For example, if your log files are rotated while a particularly large file is being downloaded, the log entry for the request will appear in the new log file when the request completes, rather than in the old log file when the request was started. In contrast, an error message is written to the error log as soon as it is encountered. Let us look at these two scenarios, individually.

Access log

The access log is created and written to by the module mod_log_config, which is not a part of the core. To use the logging facility to its fullest, we need to first discuss the three configuration directives to manage request logging.

LogFormat

This directive specifies the format of the access log file and is known as Common Log Format (CLF). The default format is:

LogFormat "%h %l %u %t "%r" %>s %b" common

The first parameter is a format string indicating the information to be logged, and the format in which it should be written; the second parameter gives the format string a name. You can decipher the log format using the symbol table available at Apache website. Here, using mod_logio, you can also measure the number of bytes transferred for every request. This feature allows hosting providers to put accurate billing mechanisms in place. The counting is done before SSL/TLS on input, and after SSL/TLS on output, so the numbers will correctly reflect any changes made by encryption.

Now that you are familiar with format strings, let’s look at commonly used log formats:

common (the default)     -       %h %l %u %t "%r" %>s %b
combined  -    %h %l %u %t "%r" %>s %b "%{Referer}i" "%{User-Agent}i"
vcommon   -    %v %h %l %u %t "%r" %>s %b
vcombined -    %v %h %l %u %t "%r" %>s %b "%{Referer}i" "%{User-Agent}i"

However, we can also create our own log format, but it is usually recommended to only use the above, as these are supported by log analysers.

TransferLog

This is the basic request logging directive, which creates an access log with the given filename:

TransferLog /var/www/logs/access_log

The filename can be given with an absolute path, as above. If a relative filename is supplied, Apache will create the full path by prepending the server home directory (e.g., /usr/local/apache). By default, the TransferLog directive uses the Common Log Format (CLF), which logs every request on a single line with the information formatted. Here is an example of what such a line looks like:

81.137.203.242 - - [29/Jun/2004:14:36:04 +0100] "POST /upload.php HTTP/1.1" 200 3229

CustomLog

This directive is used to log requests to the server. The equivalent to the TransferLog usage described above looks like what is shown below:

CustomLog /var/www/logs/access_log custom

The first argument specifies the location to which the logs will be written. The second argument specifies what will be written to the log file. It can specify either a nickname defined by a previous LogFormat directive, or it can be an explicit format string as described in the log formats section. For example, the following two sets of directives have exactly the same effect:

# CustomLog with format nickname
LogFormat "%h %l %u %t "%r" %>s %b" common
CustomLog logs/access_log common

# CustomLog with explicit format string
CustomLog logs/access_log \"%h %l %u %t \"%r\" %>s %b\"

However, the first argument can also start with a pipe character, |, followed by the path to a program to receive the log information on its standard input. As by default, Apache uses the CLF, which does not record many request parameters. At the very least, you should change the configuration to use the combined format, which includes the UserAgent and the Referer fields.

Error logs

The Apache error log contains error messages and information about events unrelated to request serving. In short, the error log contains everything the access log doesn’t:

  • Startup and shutdown messages
  • Various informational messages
  • Errors that occurred during request serving (i.e., status codes 400-503)
  • Critical events
  • Standard error output (stderr)

The format of the error log is fixed. Each line essentially contains only three fields: the time, the error level, and the message. In some rare cases, you can get raw data in the error log (no time or error level). Apache 2 adds the Referer information to 404 responses noted in the error log.

Error logs are created using the ErrorLog configuration directive. Standard file naming conventions apply here; a relative filename will be assumed to be located in the server main folder.

ErrorLog /var/www/logs/error_log

The directive can be configured globally or separately for each virtual host. The LogLevel directive configures log granularity, and ensures more information than necessary is not in the log. Events that are on the specified level or higher will be written to the log file. The default setting is warn. On server startup, you will get a message similar to the following one:

[Mon Jul 05 12:26:27 2004] [notice] Apache/2.0.50 (Unix) DAV/2
PHP/4.3.4 configured -- resuming normal operations

You will see a message to log the shutdown of the server:

[Mon Jul 05 12:27:22 2004] [notice] caught SIGTERM, shutting down

Most other relevant events will find their way to the error log as well.

The Apache error log is good at telling you that something bad has happened, but it may not contain enough information to describe it. For example, since it does not contain information about the host where the error occurred, it is difficult to share one error log between virtual hosts.

There is a way to get error messages with more information using the mechanism of custom logging. Here is an example:

LogFormat "%h %l %u %t "%r" %>s %b "%{error-notes}n"" commone
CustomLog logs/super_error_log commone

Log rotation

Logs must be rotated on a regular basis, because accumulation of logs not only uses disk space, but also opening, closing and manipulating very large log files consumes system resources and will ultimately slow down the server. Thus, to easily handle log rotation, the rotatelogs utility is shipped with Apache, which uses piped logging, and rotates the file after a specified time period (given in seconds). For example:

CustomLog "|/usr/local/apache/bin/rotatelogs /var/www/logs/access_log 300" custom

The above command rotates the log every five minutes. The rotatelogs utility appends the system time (in seconds) to the log name, to keep filenames unique. For the configuration directive given above, you will get filenames such as access_log.1089207300, access_log.1089207600, access_log.1089207900.

Given below are some more log directives that are useful while creating logs.

Logging cookies

The directives below will record all cookies sent to Apache by clients, and all the cookies Apache asks clients to set in their databases; this can be useful when debugging Web applications that use cookies. To log cookies received from the client, use the following code:

CustomLog logs/cookies_in.log "%{UNIQUE_ID}e %{Cookie}i"
CustomLog logs/cookies2_in.log "%{UNIQUE_ID}e %{Cookie2}i"

To log cookie values set and sent by the server to the client, use the code given below:

CustomLog logs/cookies_out.log "%{UNIQUE_ID}e %{Set-Cookie}o"
CustomLog logs/cookies2_out.log "%{UNIQUE_ID}e %{Set-Cookie2}o"
Note: Using the %{Set-Cookie}o format for debugging is not recommended if multiple cookies are (or may be) involved. Only the first one will be recorded in the log file.

Logging proxy requests

The directives shown below log requests that go through the proxy to a file that is different from the one from which the requests come directly to Apache. Here, use the SetEnv directive to earmark those requests that came through the proxy server, in order to trigger conditional logging:

SetEnv is_proxied 1

CustomLog logs/proxy_log combined env=is_proxied

Logging the server IP address

Often, Apache is configured with virtual hosts with multiple addresses. Here, the directives to log the IP address of the virtual host that replied to the request will be as follows:

CustomLog logs/served-by.log "%{UNIQUE_ID}e %A"

Miscellaneous other ways to secure Apache

Use mod_parmguard

Definitely the most useful of the Apache modules, mod_parmguard (parameter guard) inspects incoming form submissions for abnormally-set parameters. The module includes a script that spiders your Web application, building up a profile of all forms in use. You can use this profile directly, or instead, tune it for better detection. For instance, the script might make sure that a parameter only got numeric values, but you could force those numeric values to be between 1 and 5.

Configuring the module from Apache’s point of view is extremely simple, because it has only three directives. The important part is the module’s configuration file, which is based on XML, and is the heart of mod_parmguard. mod_parmguard has only two server-level directives (ParmguardConfFile and ParmguardTrace) and one location-wide directive (ParmguardEngine).

ParmguardConfFile sets the location for the module’s configuration file. In this case, it would be /usr/local/apache1/conf/mod_parmguard.xml:

<IfModule mod_parmguard.c>
	ParmguardConfFile /usr/local/apache2/conf/mod_parmguard.xml
</IfModule>

You should have at least a minimal mod_parmguard.xml file set up. Here is an example:

<?xml version="1.0"?>
<!DOCTYPE parmguard SYSTEM "mod_parmguard.dtd">
<parmguard>
<!-- requested action when there is a mismatch -->
<global name="scan_all_parm" value="1"/>
<global name="illegal_parm_action" value="accept,log,setenv"/>
</parmguard>

For now, don’t worry about the meaning of the options in this file. You must also remember to copy the file mod_parmguard.dtd to the same directory as mod_parmguard.xml, otherwise the XML parser will complain and won’t let Apache start.

The ParamguardTrace directive is used in case you have a problem with the module, and you want to understand exactly what is going on. The only available option is debug. For example:

<IfModule mod_parmguard.c>
	ParmguardTrace debug
	ParmguardConfFile /usr/local/apache2/conf/mod_parmguard.xml
</IfModule>

The debug messages will be sent to Apache’s error log.

The ParamguardEngine directive sets the location in which the engine actually is. This option must be placed in a <Location> directive in your httpd.conf (it will have no effect if placed in a directive). For example:

<Location /cgi-bin/>
	ParmguardEngine on
</Location>

Finally, as far as your httpd.conf is concerned, your module’s configuration should be similar to what follows:

<IfModule mod_parmguard.c>
#ParmguardTrace debug
ParmguardConfFile /usr/local/apache2/conf/mod_parmguard.xml
</IfModule>

<Location /cgi-bin/>
#ParmguardEngine on
</Location>
Note: The directive ParmguardEngine is commented out for now, as no proper filter has been set yet.

Hide Apache version and other sensitive information

Attackers can use this information to their advantage when performing attacks. It also sends the message that you have left most defaults alone. There are two directives that you need to add, or edit in httpd.conf:

ServerSignature Off
ServerTokens Prod

The ServerSignature appears on the bottom of pages generated by Apache, such as 404 pages, directory listings, etc. The ServerTokens directive is used to determine what Apache will put in the Server HTTP response header. By setting it to Prod, it sets the HTTP response header as follows:

Server: Apache

Ensure that files outside Web root are not served

Apache should not provide access to any files outside its Web root. Assuming all your websites are placed under one directory (we will call this /web), you would set it up as follows:

<Directory />
  Order Deny,Allow
  Deny from all
  Options None
  AllowOverride None
</Directory>
<Directory /web>
  Order Allow,Deny
  Allow from all
</Directory>
Note: As we set Options None and AllowOverride None, this will turn off all options and overrides for the server. You now have to add them explicitly for each directory that requires an Option or Override.

Do not allow Apache to follow symbolic links

This again, can be done using the Options directive, inside a Directory tag. Set Options to either None or -FollowSymLinks.

Only give the root user read access to Apache’s config and binaries

Assuming your Apache installation is located at /usr/local/apache, this can be done as follows:

chown -R root:root /usr/local/apache
chmod -R o-rwx /usr/local/apache

We will be back with other interesting articles on security and hacking.

Always remember: Know hacking, but no hacking.

  • Sylvain Letede Ongolo

    very good docs

  • Nilesh Dhodad

    series ends hun

  • tasy

    i kept myself to comment at the end of series only. hope you mind.
    i just want to say…’hats off to you man’ thank you very much and keep us educating..thanks

  • Tim Broberg

    Apache didn’t like the httpd.conf LogFormat line above as the repeated double quotes got interpreted as many arguments when he expected no more than two.
    Escaping the internal quotation marks with backslash did teh trick.
    LogFormat “%h %l %u %t “%r” %>s %b “%{error-notes}n”" commone

All published articles are released under Creative Commons Attribution-NonCommercial 3.0 Unported License, unless otherwise noted.
Open Source For You is powered by WordPress, which gladly sits on top of a CentOS-based LEMP stack.

Creative Commons License.