Introducing mod_security
by Ivan Ristic11/26/2003
Running public web applications may seem like playing Russian roulette. Although achieving robust security on the Web is possible in theory, there's always a weak link in real life. It only takes one slip of the code to allow attackers unrestricted access to your data. If you have a public web application of modest complexity running, chances are good that is has some kind of security problem. Take this URL for example:
http://www.webapp.com/login.php?username=admin';DROP%20TABLE%20users--
If your application is vulnerable to SQL injection, invoking the URL above may very well delete all user data from your application. Do you make regular database backups?
Fortunately, the mod_security Apache module can protect you from this and other forms of web attacks.
Why Would You Use mod_security?
A year and a half ago, before I started working on mod_security, I used
Snort to monitor my web traffic. It worked very well; I told Snort which
keywords I was interested in and it alerted me every time one appeared in the
data stream. But I wanted more. I wanted the freedom to specify complex rules
and perform various HTTP related actions. Besides, having an IDS installed
wherever a web server exists is very time consuming and expensive.
At the time I also tried the combination of mod_rewrite and mod_setenvif.
Using mod_rewrite it is very easy to detect the words drop and
table, and then redirect the client away from the original URL, preventing the attack. However, while that would certainly keep away less knowledgeable attackers, a determined attacker could simply invoke the same URL
as above but use the POST method instead of GET. Since POST variables are not
considered in the normal processing of most modules, the attack would go through.
Having established the need to build a new tool, I faced two choices: go with Java and create a full-blown reverse proxy and application gateway application, or create an Apache module, building on top of a large amount of existing code. Option one would require a lot of work and probably result in something very few people would want to use (hey, I wouldn't use it either). I wanted to build something flexible and easy to use, so I chose the latter. I've never looked back.
Going back to our URL example, to prevent the "drop table" SQL injection
attack with mod_security, add the following to your Apache configuration:
SecFilter "drop[[:space:]]table"
The only parameter is a regular expression to be applied to the incoming
request. This seems achievable with mod_rewrite, but the difference here is
that mod_security will detect and prevent attacks performed using either GET or
POST. As it turns out, adding the ability to monitor POST requests was a very
big problem for Apache 1.3.x since it does not support a notion of filters.
Installation and Configuration
The best way to install mod_security is to compile it from the source code
(or, if you are running Apache on Windows and don't have a compiler around go
to the web site and download a pre-compiled dll):
$ /path/to/apache/bin/apxs -cia mod_security.c
# /path/to/apache/bin/apachectl stop
# /path/to/apache/bin/apachectl start
Before you do that you need to add few lines to the configuration file:
<IfModule mod_security.c>
# Turn the filtering engine On or Off
SecFilterEngine On
# Make sure that URL encoding is valid
SecFilterCheckURLEncoding On
# Unicode encoding check
SecFilterCheckUnicodeEncoding Off
# Only allow bytes from this range
SecFilterForceByteRange 0 255
# Only log suspicious requests
SecAuditEngine RelevantOnly
# The name of the audit log file
SecAuditLog logs/audit_log
# Debug level set to a minimum
SecFilterDebugLog logs/modsec_debug_log
SecFilterDebugLevel 0
# Should mod_security inspect POST payloads
SecFilterScanPOST On
# By default log and deny suspicious requests
# with HTTP status 500
SecFilterDefaultAction "deny,log,status:500"
</IfModule>
I've left the comments in the code so it should be pretty evident what
directives do. This configuration will activate mod_security but it won't do
much. It is always a good idea to start with a relaxed configuration and build
into a more restrictive one.
|
Related Reading
Apache Cookbook |
So What Does this Do?
Even with the relaxed configuration, mod_security will still provide two
benefits. First, it will perform a series of anti-evasive techniques and will
canonicalize the input. This will help later when you start adding filtering
rules to the configuration. Imagine you want to prevent people from executing
a ps binary on the server, using a regular expression such as
/bin/ps ax. This expression would catch simple invocations but
perhaps not /bin//ps ax or /bin/ps%20ax or
/bin/./ps ax. Here is a list of what mod_security does here:
- Remove multiple forward slashes (
//). - Remove self-referenced directories (
./). - Treat
\and/equally (on Windows only). - Perform URL decoding.
- Replace null bytes (
%00) with spaces.
I am also thinking about replacing all consecutive white space characters with spaces, but I am not yet sure about it.
The other benefit comes from certain built-in checks:
- URL encoding validation.
- Unicode encoding validation.
- Byte range verification, where only certain character values are allowed as part of a request.
Actions
Whenever a rule match occurs a series of actions is performed. The default
action list (configured through SecDefaultAction) is used in most
cases. It is also possible to specify per-rule actions by supplying a second
parameter to SecFilter or a third parameter to
SecFilterSelective. Supported actions are:
deny, deny the requestallow, stop rule processing and allow the requeststatus:nnn, respond with a HTTP status nnnredirect:url, redirect the request to the absolute URL urlexec:cmd, execute a script cmdlog, log the request to the error lognolog, do not log the requestpass, ignore the current rule match and go to the next rulepause:nnn, stall the request for nnn milliseconds. Be very careful with this action; one Apache instance will be busy stalling the request. You could actually help the attackers in creating a denial of service attack.
Other actions affect the flow of the rules, similarly to how mod_rewrite
works:
chain, go to evaluate the next rule in the chain. When one rule fails to trigger an alert the remaining rules from the chain will be skipped.skipnext:n, skip the next n rules.
Filtering Rules
Rules come in two flavors. In the simplest form,
SecFilter keyword
will apply the keyword (a regular expression) to the first line of the
incoming request (the one that looks like GET /index.php HTTP/1.0)
and to the POST payload if it exists. It is a pretty broad rule whose purpose
is mostly to be used as a first step when rules are introduced in articles like
this one. You should instead use:
SecFilterSelective "variable list separated with |" keyword
as it allows much better control over what should be analysed (and spends less CPU cycles doing it). Instead of continuing to bore you with the syntax I will now present a series of interesting examples. Let them serve as inspiration; the most useful rules usually come from dealing with real-world problems.
This rule will allow all requests from a single IP address (representing my workstation) through. No other rules will be processed. Since such requests do not represent attacks this rule match will not be logged:
SecFilterSelective REMOTE_ADDR "^IP_ADDRESS_HERE$" nolog,allow
This rule allows me full access from my laptop when I am on the road.
Because I don't know what your IP address will be, access is granted to all
clients having a string Blend 42 in the User-Agent field. This is
poor protection on its own but can be pretty interesting on top of some other
authentication method.
SecFilterSelective HTTP_USER_AGENT "Blend 42"
This rule prevents SQL injection in a cookie. If a cookie is present, the request can proceed only if the cookie only contains one to nine digits.
SecFilterSelective COOKIE_sessionid "!^(|[0-9]{1,9})$"
This rule requires HTTP_USER_AGENT and HTTP_HOST
headers in every request. Attackers often investigate using simple tools (even
telnet) and don't send all headers as browsers do. Such requests can be
rejected, logged, and monitored.
SecFilterSelective "HTTP_USER_AGENT|HTTP_HOST" "^$"
This rule rejects file uploads. This is simple but effective protection, rejecting requests based on the content type used for file upload.
SecFilterSelective "HTTP_CONTENT_TYPE" multipart/form-data
This rule logs requests without an Accept header to examine
them later; again, manual requests frequently do not include all HTTP headers.
The Keep-Alive header is another good candidate.
SecFilterSelective "HTTP_ACCEPT" "^$" log,pass
This rule will send me an email when the boss forgets his password again.
We have two rules here. The first will trigger only when one specific file is
requested (the one showing the "Login failed" message. The second rule will
then check to see if the username used was ceo. If it was, it will
then execute an external script.
SecFilterSelective REQUEST_URI "login_failed\.php" chain
SecFilterSelective ARG_username "^ceo$" log,exec:/home/apache/bin/notagain.pl
This rule sends Google back home by redirecting Googlebot somewhere else, based on the User-Agent header. It does not log rule matches.
SecFilter HTTP_USER_AGENT "Google" nolog,redirect:http://www.google.com
This rule checks all variables for JavaScript, allowing it in a variable
named html. Disallowing JavaScript in all variables can be very
difficult for some applications (most notably CMS tools). By using this rule we
disallow JavaScript in all variables except in the one named html,
where we know it can appear.
SecFilter "ARGS|!ARG_html" "<[:space:]*script"
Finally, this example shows how you can have multiple mod_security
configurations. This means you can tailor rules for a specific
application. Note the usage of the directive SecFilterInheritance.
With it we tell mod_security to disregard all rules from the parent context and
start with a clean slate.
<Location /anotherapp/>
SecFilterForceByteRange 32 126
# Use this directive not to inherit rules from the parent context
SecFilterInheritance Off
# Developers often have special variables, which they use
# to turn the debugging mode on. These two rules will
# allow the use of a variable "debug" but only coming from
# the internal network
SecFilterSelective REMOTE_ADDR "!^192.168.254." chain
SecFilterSelective ARG_debug "!^$"
</Location>
Performance Considerations
I have never had any performance problems with mod_security. In my
performance tests the speed difference was around 10 percent. However, the
practical performance penalty is smaller. On real web sites, a single page
request may provoke many static requests for images, style sheets, and
JavaScript libraries. Mod_security is smart enough not to look at those only if
you tell it not to:
SecFilter DynamicOnly
The bottleneck is always in the IO operations. Make sure that the debugging
mode is never turned on on a production server, and avoid using the full audit
logging mode unless you really need to. In the configuration above,
mod_security is configured to only log relevant requests, e.g., those that have
triggered a filter.
Other Features
Internal chroot
If you have ever tried to chroot a web server you probably know that it is
sometimes a complex task. With mod_security the complexity goes away. You are
one configuration directive away from a chrooted server:
SecChrootPath /chroot/home/web/apache
The only requirement is that the web server path in the chroot be the same
as the path outside of the chroot (in the example above,
/home/web/apache). In addition to making chrooting very easy, this
approach will allow you to have a chroot that contains only data files
without binaries. This advantage comes from the fact that the chroot call is
executed internally, after all the dynamic libraries are loaded and log files
opened.
Changing Server Signature
Attackers and automated scripts frequently learn about the server and the
version from the "Server" HTTP header that is delivered with every response.
You can change only change this by changing the Apache source code, but you can
also use this directive. (You should use this feature only if you're running
Apache 1.x. Module mod_headers included with Apache 2.x should be able to
intercept outgoing headers, and change them on the fly):
SecServerSignature "Microsoft-IIS/5.0"
What Next?
Although I compared mod_security to Snort at the beginning of this article,
mod_security is just another tool in your security belt. It works best
together with an IDS operating on a network level. Its biggest advantage is in
filling the gap between the web server and the application, allowing you to
protect your applications without actually touching the source code.
While you are reading this article I am busy working on a couple of new and
very interesting features. First of all, I want to complete
multipart/form-data encoding support. Once that is done, you will
be able to intercept file uploads and run checks on files (using external
binaries), with an option to reject them for any reason. Even more interesting
is a feature called a "Application Armour," a special form of application lockdown where for each script you will be able to specify and verify every
incoming parameter (you won't need to do it manually, don't worry).
In the meantime, please send me your comments and requirements to influence
the way mod_security develops.
References
Ivan Ristic is a Web security specialist and the author of ModSecurity, an open source intrusion detection and prevention engine for web applications, and the author of O'Reilly's Apache Security.
Return to Apache DevCenter.
-
Hi
2010-04-27 18:16:35 navbzy [View]
-
mod_security configuration
2009-07-06 12:59:17 paes [View]
-
mod_security configuration
2009-07-06 12:58:47 paes [View]
-
Mod_Security2 Configuration
2008-09-27 15:59:39 selecta [View]
-
Problem with host
2007-10-18 07:42:24 dogheart [View]
-
problem turning off rule selectively
2007-10-07 12:56:51 SalemDesign [View]
-
mod security versions ass/ apache version
2007-07-31 07:13:47 Safari-DM [View]
-
mod_security with OAS
2006-11-15 07:33:08 mclaugb [View]
-
mod_security2 and SecRule
2006-10-31 08:45:33 monicat [View]
-
issue with mod_security2
2006-10-30 10:31:56 monicat [View]
-
filettering php shell Scripts
2006-02-21 09:58:35 ALDWLYA [View]
-
Relationship with gateway? Embedded firewall vs. gateway
2004-12-22 16:07:13 Meshfire [View]
-
Tracking stateful session information is important for Web application security firewall appliance
2004-12-18 16:00:19 Meshfire [View]
- Trackback from http://joescomp.dyndns.org/index.php?/archives/16-An-Apache-IDS-module-I-found-...html
An Apache IDS module I found ..
2004-10-27 09:58:59 [View]
- Trackback from http://weblog.cemper.com/a/200402/27-apache-mod-security-introduction.php
Apache mod_security introduction
2004-02-26 15:17:26 [View]
-
Brave work! ----- from Roboo
2003-12-11 17:08:49 anonymous2 [View]
-
small typo in the article
2003-12-03 07:21:46 anonymous2 [View]
-
This looks good
2003-12-02 13:25:59 anonymous2 [View]
-
bad application design shouldn't drive new development
2003-12-01 01:02:04 anonymous2 [View]
-
Wonderful module
2003-11-28 04:27:56 anonymous2 [View]