...making Linux just a little more fun!

Securing Apache Web Server with mod_security

By René Pfeiffer

The Internet has its share of packet filters and proxy servers in order to increase the security for clients and servers alike. Filtering network traffic is never a bad idea since it provides a basic level of protection. When it comes down to protecting web servers your packet filter will most probably allow HTTP and HTTPS traffic to your server application. Unless you deploy an application proxy that inspects HTTP you can't do more. But you can equip your Apache web server with mod_security which in turns helps you to analyse any request thrown at it.

Application Layer Inspection

When you do any network traffic filtering or inspection you have to keep in mind that usually nothing understand the things that should be inspected better than the application in question. This is one of the reasons why proxy filters are "better" suited for this job. They know the protocol and can normalise fancily encoded requests. mod_security is in a very similar position. It sits right inside the Appache httpd process and inspects the HTTP requests. This is an important advantage over proxies since it can also see compressed or even encrypted content without difficulties.

So, what needs to be inspected? Apache's httpd surely does inspect HTTP requests. What do I need more? Well, there are some things mod_security can do for you.

Sounds pretty impressive if you ask me. Now we only need to know how to add it to an existing Apache deployment.

Installation

The current released version is 2.x. It works well with Apache 2.0.x and 2.2.x. Apache 1.3.x is not supported anymore (you should really upgrade your Apache servers, seriously). mod_security has some more requirements.

mod_security doesn't use autoconf. You have to check its Makefile and tell it where the ServerRoot directory of your Apache installation is. Then you can try make and see if everything compiles. If you get compilation errors, make sure your compiler environment and your development packages are complete. After the make command finishes, stop your Apache server and issue a make install. The Makefile will copy the module into the Apache server modules directory (usually /usr/local/apach2/modules/ for a compiled web server, your distribution may put the modules elsewhere). Now you only need to active the module by adding the following lines to your Apache configuration.
LoadFile /usr/lib/libxml2.so # optional
LoadModule security2_module modules/mod_security2.so
There we go. The only thing we need is to configure the module and the rule sets.

Configuration

One word of caution: Every security measure must be applied with a specific purpose. You can't just add filters without thinking about the consequences for applications. You or your users may have web applications running that break when special security measures are activated. If you are not sure about not breaking something you can use all rule sets and actions in "audit mode". Then mod_security will only log but not block. This is a good idea to test everything until you are sure to switch to "live mode". It also keeps your users happy.

A very simple test is to add a single rule by using the following two lines:

SecRuleEngine On
SecRule REQUEST_URI attack
Now send your web server a request with the word attack in it. You should get the error code 403 Forbidden and the blocked request should generate an entry in Apache's error log file. If you set the first option to
SecRuleEngine DetectionOnly
then the module will only detect and block nothing. We will now take a look at the classes of different options available. Please make sure you take a look at mod_security's documentation and at the file in the sample core rules archive that can be downloaded.

General Options

mod_security has several groups of options. Here are some of the basic configuration directives.

SecRuleEngine On
SecRequestBodyAccess On
SecResponseBodyAccess On
The first line switches request inspection on or off. The other two lines control whether request and response body data will be inspected by the module. Keep in mind that the data has to be buffered. This means that it is required for inspecting HTTP POST requests, but it has to be buffered and thus needs memory. You can reduce the amount of memory used by this directive.
SecRequestBodyInMemoryLimit 131072
You can also limit the size of the HTTP request body data. This is very handy for disabling large data in HTTP POST requests.
SecRequestBodyLimit 10485760
Every request bigger than 10485760 byte will be answered by the error code 413 Request Entity Too Large. The default is 134217728 bytes (131072 KB).

Web servers typically include the MIME type of data they put into responses. You can control the types you want to inspect. Usually you will not want to parse JPEG or PDF data.

SecResponseBodyMimeTypesClear
SecResponseBodyMimeType (null) text/plain text/html text/css text/xml
The first statement clears the list of types to be checked. The second line sets the types we are interested in. File uploads may be something you wish to control. You can redirect every file upload to a seperate directory. In addition you can collect all uploaded files to your server provided you have the disk space. This may be useful for forensic activities after something has gone wrong.
SecUploadDir /var/spool/apache/private
SecUploadKeepFiles Off
It is good practice not to use the usual directories for temporary files for storing uploads. Create a directory for this purpose and set the permissions accordingly. Only Apache needs to access this space.

Logging

You can enable audit logging which can help you a lot during debugging or worse situations. You have the choice of logging everything or only events that triggered one of mod_security's internal checks. This includes the rule sets.

SecAuditEngine RelevantOnly
SecAuditLogRelevantStatus "^[45]"
We want to log only relevant events and concentrate on responses that generate 4xx or 5xx status codes. That's what the regular expression is for. You can do a full logging if you need to. The log can be written into a single file or into a directory with one file per log event. The latter is usually only for high volume sites.
SecAuditLogType Serial
SecAuditLog /var/log/www/modsec_audit.log
# SecAuditLogStorageDir /var/log/www/modsec_audit
As mentioned before the content that can be logged is extended. You can log portions of the original HTTP request along with the response.
SecAuditLogParts "ABEIFHZ"
This means we want to log the audit header and trailer (options A and H), the original request (option B), an intermediate response body (option E), the modified request body (option I), the final response header (option F) and the final boundary of the log event (option Z, which is mandatory). The intermediate response body is either the original response or the modified response by mod_security. The modified request body is either the original request body or a shortened variant whenever multipart/form-data encoding was used. The default options are "ABIFHZ".

Rules

The security module uses five distinct phases for processing requests to and responses from the web server.

  1. Parse request headers
  2. Parse request body
  3. Parse response headers
  4. Parse response body
  5. Do logging
It is important to keep this in mind when designing rule sets, almost just as in designing packet filters. The directive SecRule describes a rule.
SecRule VARIABLES [OPERATOR] ACTIONS
When written in this form mod_security will
  1. expand the variables,
  2. apply the operator if present,
  3. trigger once for a match in every variable,
  4. and execute
    1. the default action or
    2. the actions described in the rule.
Remember the test rule with the string "attack". We told the module to check the variable REQUEST_URI of the HTTP request and apply the regular expression operator consisting of the desired string to look for. We didn't give any action, so the default action applies. You can combine variables by using logical operators.
SecRule "REQUEST_URI|QUERY_STRING" attack
This does the same but with two different variables. The action will be triggered if the string fragment is found in either variable. You can use well known operators inside rules. Matching of regular expressions is done by the PCRE library, so you can use any constructs PCRE understands (which is basically everything you can do in Perl's pattern matching). Long lines can be split by using "\" just as in Bash shell scripts.

Keep in mind that the VARIABLES section contains variables. Their content changes. If the variable is empty or not present, the rule doesn't match. This is importent and desired for parameter checking. Variables can be ARGS, FILES, FILES_TMPNAMES, ENV, REMOTE_PORT, QUERY_STRING, SCRIPT_BASENAME, AUTH_TYPE, REQUEST_COOKIES, SESSIONID, TIME and many more. The reference has a complete list.

Operators

mod_security rules can contain operators. They are used to validate the request or look for special anomalies. The "@" indicates that an operator follows.

SecRule ARGS "@validateUtf8Encoding"
SecRule ARGS "@validateByteRange" 10,13,32-126
The first rule checks the request for valid UTF-8 encoding. The second example checks for a specific range of characters in the request. If the request contains other characters than linefeed, carriage return or the US ASCII characters then the action is triggered. You can also invoke additional scripts.
SecRule FILES_TMPNAMES "@inspectFile /usr/local/bin/check_file.pl" 
This redirects any uploaded files to the Perl script for further checks. The exit code of the script tells mod_security whether to invoke the action or not. You can even use realtime blacklists for your rules.
SecRule REMOTE_ADDR "@rbl bad.guys.and.girls.example.net"

Actions

There are five general types of actions.

  1. Disruptive actions - abort current transaction.
    • deny - stops transaction and generates an error.
    • drop - drops transaction without error.
    • redirect - responds with a redirection (such as 301 or 302).
    • proxy - forwards the request to another server.
    • pause - slows down the execution of the request.
  2. Non-disruptive actions - change the state of the current transaction.
  3. Flow actions - change the flow of the rules.
    • allow - stops processing of subsequent rules.
    • chain - combines the active rule with the next one.
    • pass - ignores a match in the current rule; useful for commenting rules out, but leave them still active.
    • skip - skip the next or more rules.
  4. Meta-data actions - contain meta data for rules as additional information; useful for logging.
  5. Data actions - are placeholders for other actions.
The default action can be set with the directive SecDefaultAction. It can be changed whenever you need it, so you can define blocks of rules with different default actions. The actions are very similar to the ones used by the intrusion detection/prevention software Snort. They give you a lot of flexibility and allow for quite complex coverage of tests. Here's is one example from the core rules that look for attempts to inject an email message into the request.
SecRule REQUEST_FILENAME|ARGS|ARGS_NAMES|REQUEST_HEADERS|XML:/* "[\n\r]\s*(?:to|bcc|cc)\s*:.*?\@" \
  "t:none,t:lowercase,t:urlDecode,capture,ctl:auditLogParts=+E,log,auditlog,msg:'Email Injection Attack.
  Matched signature <%{TX.0}>',,id:'950019',severity:'2'"</%{TX.0}>
Note that I inserted a line break for better readability. The action parameter is a single string and tell the module to log every match to the normal log and the audit log with the message text Email Injection Attack along with some parameters of the request. The core rules have more examples for other attacks such as cross-site scripting, SQL injection, HTTP anomalies and the like.

Rule Management

Make sure that you keep your rule files in good order and make sure that you document every change. This is very important. Any kind of filter can break whole applications and protocols. Therefore you need to now what changes caused which effects. You will also need this information when developing your own rules. Bear in mind that custom web applications need custom rules. Some attacks may be the same, but customised applications have their peculiarities that have to be considered. A good place to start are the core rule sets. You can disable rules without deleting them from the configuration. This is extremely useful in case you wish to distribute rules to multiple servers. You can do that by splitting your rule into multiple files and have a master configuration that enables or disables selected sets.

Performance and Deployment

Everything has a price and so does filtering HTTP requests. mod_security needs to holds the request in a buffer or has to store it to a temporary file. You have to take this into account. The parsing add a little overhead in terms of CPU cycles to the web server as well. If you install the module on a server that already has performance issues things won't get better. That's what the reverse proxy method is for. Hard hit sites probably won't go anywhere without additional proxies.

One last thing to keep in mind are your own web applications. Don't just set up the core rules and accept all defaults. Inspect the rule sets and decide for yourself if you need all the rules. Things can break if you are not careful enough. No one knows your web apps better than you. Use this knowledge to your advantage.

Useful resources

Talkback: Discuss this article with The Answer Gang


Bio picture

René was born in the year of Atari's founding and the release of the game Pong. Since his early youth he started taking things apart to see how they work. He couldn't even pass construction sites without looking for electrical wires that might seem interesting. The interest in computing began when his grandfather bought him a 4-bit microcontroller with 256 byte RAM and a 4096 byte operating system, forcing him to learn assembler before any other language.

After finishing school he went to university in order to study physics. He then collected experiences with a C64, a C128, two Amigas, DEC's Ultrix, OpenVMS and finally GNU/Linux on a PC in 1997. He is using Linux since this day and still likes to take things apart und put them together again. Freedom of tinkering brought him close to the Free Software movement, where he puts some effort into the right to understand how things work. He is also involved with civil liberty groups focusing on digital rights.

Since 1999 he is offering his skills as a freelancer. His main activities include system/network administration, scripting and consulting. In 2001 he started to give lectures on computer security at the Technikum Wien. Apart from staring into computer monitors, inspecting hardware and talking to network equipment he is fond of scuba diving, writing, or photographing with his digital camera. He would like to have a go at storytelling and roleplaying again as soon as he finds some more spare time on his backup devices.


Copyright © 2007, René Pfeiffer. Released under the Open Publication License unless otherwise noted in the body of the article. Linux Gazette is not produced, sponsored, or endorsed by its prior host, SSC, Inc.

Published in Issue 143 of Linux Gazette, October 2007

Tux