Nuts and bolts ModSecurity Lead image: © KrishnaKumar Sivaraman, 123RF.com
© KrishnaKumar Sivaraman, 123RF.com
 

Protecting web servers with ModSecurity

Apache Protector

Even securely configured and patched web servers can be compromised because of vulnerabilities in a web application. ModSecurity is an Apache extension that acts as a web application firewall to protect the web server against attacks. By Sebastian Wolfgarten

Security issues on the web are no longer typically a result of poor configuration or the lack of up-to-date server software. Tomcat, Apache, and even IIS have become extremely mature over the past few years – so much so that they don't have any noticeable vulnerabilities, although exceptions can always turn up to prove the rule. Thus, hackers have turned their attention to the web applications and scripts running on the servers.

Increasingly complex user requirements are making web applications more complex, too: Ajax, interaction with external databases, back-end interfaces, and directory services are just part of the package for a modern application. And, attack vectors grow to match this development (see the "Attacks on Web Servers" box).

Firewalls for the Web

In contrast to legacy packet filters, Web Application Firewalls (WAFs) don't inspect data in the network or transport layer, but rather at the HTTP protocol level (i.e., in OSI Layer 7) [1]. They actually speak HTTP. For this to happen, these firewalls analyze incoming and outgoing client requests and server responses to distinguish between benevolent and malevolent requests on the basis of rules. If necessary, they can even launch countermeasures; if configured to do so, the software will also inspect encrypted HTTPS connections.

Accessories en Masse

Where classical network-based firewalls – I'm exaggerating slightly here – either permit any or no HTTP connections, WAFs target individual HTTP connections based on their content. ModSecurity is a high-performance WAF for Apache and a complex module for the Apache web server. Originally developed by Ivan Ristic, Breach Security handles its distribution and development [2].

Two variants of the software are available: the open source variant released under the GPLv2, and a commercial version with professional support, pre-configured appliances, and management consoles. ModSecurity runs on Linux, Solaris, FreeBSD, OpenBSD, NetBSD, AIX, and Windows, with the later versions only available for Apache 2.x. This article discusses version 2.5.10; the successor 2.5.11 is merely a bugfix.

The software's functional scope is enormous but comprehensively documented [3]. It logs HTTP requests and gives administrators unrestricted access to the individual elements of a request, such as the content of a POST request. It also identifies attacks in real time based on positive or negative security models and detects anomalies based on supplied patterns for known vulnerabilities.

The powerful rules discover whether credit cards are in the data stream or use GeoIP to prevent access from certain regions. ModSecurity checks not only incoming requests but also the server's outgoing responses. The software can implement chroot environments. As a reverse proxy, it protects web applications on other web servers, such as Tomcat or IIS.

Breach also provides a collection of core rules that guarantees the basic security of the web server. Comprehensive documentation, many examples, and a mailing list provide support for the user. This makes ModSecurity a good choice for protecting web servers and their applications against vulnerabilities. But before you can even consider tackling the highly complex configuration, you first need to install the third-party module.

Packages for any Distribution

If you prefer not to build the package for Apache 2 yourself, you can pick up pre-built packages for Debian, RHEL, CentOS, Fedora, FreeBSD, Gentoo, and Windows. The manual install requires the Apache mod_unique_id module, which is not automatically provided by some distributions. You can use the

LoadModule security2_module modules/mod_security2.so

directive to integrate the module into the Apache configuration file, httpd.conf, if your distribution doesn't do this for you. After restarting, the web server lists the module in its error_log.

Filter Rules

ModSecurity has a mass of configuration options, but to understand how it works, all you need is the basic configuration. The SecRuleEngine option activates the module's filter mechanism and allows it to process filter rules. The settings here are On, Off, and DetectionOnly, which tells the module to monitor, but not become actively involved with, the client-server connection, even if individual rules are configured to let it do so. This setting is useful for testing the module and your own rules.

To help you get started, or for debugging, you also want to enable the SecDebugLog option to define a troubleshooting log (e.g., SecDebugLog /var/log/httpd/modsec_debug.log). Additionally, you can set the SecDebugLogLevel parameter to specify verbosity, on a scale of 0 to 9, which issues comments on its own activities and the way it processes user-defined rules. Levels 4 or 5 are useful for fine tuning or troubleshooting. In production, set this parameter to 0.

The SecDefaultAction parameter defines ModSecurity's default behavior for requests that match a filter rule without a corresponding action. The option also expects you to specify a processing phase for the standard action, as listed in Table 1. ModSecurity applies filter rules in five different phases of processing and responding to a client request [4]. In real life, only phase 2, in which the client requests that content (i.e., incoming data) is filtered, and phase 4, which handles the server response (i.e., outgoing data), are relevant. Additionally, you can use the option to define one or multiple actions that the software will perform when a match occurs [5]. To log incoming client requests in the auditlog in phase 2 and respond to the access attempt with a HTTP 403 (Forbidden) error message, you would do this:

Tabelle 1: ModSecurity Processing Phases

Number

Phase

Designator

Activities

1

Preview phase

REQUEST_HEADERS

Earliest possible filtering of incoming requests before access control, authentication, authorization, and MIME detection have taken place Apache-side

2

Client request

REQUEST_BODY

Full access to the content of a client request (normal case)

3

POST request

RESPONSE_HEADERS

Initial option for filtering server responses

4

Server response

RESPONSE_BODY

Full access to the content of the server response to an incoming client request

5

Logging

LOGGING

Access to all relevant information before it is written to the Apache logfiles

SecDefaultAction phase:2,log, auditlog,deny,status:403

A massively negative default function like the default deny is highly restrictive, but it does offer maximum protection if you additionally define filters and actions (see also the "Insider Attacks" box). The software offers a number of prebuilt alternatives here, including converting request parameters, running external scripts (e.g., to perform an antivirus scan), or forwarding malevolent requests. The latter is useful if you are trying to investigate attacks or want to forward them to a honeypot.

The basic configuration (Listing 1) also logs the contents of incoming requests and the responses given in return. It enters the information in the auditlog as mentioned previously. The first directive, SecAuditEngine, enables this. The option for the second directive defines whether the software stores the auditlog entries in a single file (Serial) or writes a file for each transaction (Concurrent). Concurrency is necessary if you intend to deploy the ModSecurity Console add-on product. Breach Security offers the software for managing multiple instances, provided you don't need to monitor more than three servers.

Listing 1: Basic Configuration for ModSecurity

01 SecRuleEngine On
02 SecAuditEngine On
03 SecAuditLogType Serial
04 SecAuditLog logs/audit.log
05 SecAuditLogParts ABCFHZ
06 SecDebugLog logs/debug.log
07 SecDebugLogLevel 5
08 SecRule REQUEST_URI "/etc/passwd"
09 SecDefaultAction phase:2,log,auditlog,deny,status:403

The third instruction defines the auditlog storage location relative to the Apache installation path. Finally, the SecAuditLogParts instruction defines the information that ModSecurity logs in the auditlog (Table 2). In this case, this is the header and the content of the request, along with the ModSecurity reaction. The results are shown in Figure 1.

Tabelle 2: SecAuditLogParts Arguments

Abbreviation

Description

A

Header for the entry (mandatory)

B

Request header

C

Request content; only available if content exists and ModSecurity is configured to store it

D

Reserved

E

Temporary response content; only available if ModSecurity is configured for this

F

Final response header after possible manipulation by ModSecurity; Apache itself writes the Date and Server headers

G

Reserved

H

Auditlog trailer equivalent to C, except where the request contains form data; in this case, the software constructs a suitable request that excludes file content to simplify matches

J

Reserved

K

Line by line list of all matching rules in the order of their application

Z

End of entry (mandatory)

Once ModSecurity has been enabled, it will log suspicious activity at the detail level specified in SecAuditLogParts – in this case, an SQL injection attack.
Figure 1: Once ModSecurity has been enabled, it will log suspicious activity at the detail level specified in SecAuditLogParts – in this case, an SQL injection attack.

After this preparation, you can add the most important directive to your configuration: SecRule. This defines a filter rule and optionally an action that the module will perform if it discovers a match for the rule. If you don't define an action, the tool will run the standard command defined in the SecDefaultAction directive. Rules always follow this pattern:

SecRule Variable Operator [Action]

The number of variables is huge, and they cover every single element of the client request (both for POST and for GET), as well as the most important server environment details [6]. Additionally, you can use regular expressions. For example, to investigate an HTTP request to find out whether the client requests the /etc/passwd string in a GET method, you would use this rule:

SecRule REQUEST_URI "/etc/passwd"

If the request matches the rule, ModSecurity runs the default deny action. To filter by browser type, you would do this:

SecRule REQUEST_HEADERS: User-Agent "nikto"

This example tells the web server to refuse requests from the Nikto security scanner [7]. If you want ModSecurity to run a specific action for a rule, you can overwrite the default action:

SecRule REQUEST_HEADERS:User-Agent"nikto" "phase:2,pass,msg:'Nikto-Scan logged'"

This rule tells the module to write a Nikto scan logged message to the logfile when it detects the Nikto user agent in a client request in phase 2. The rule then overwrites the drop default action, which is defined by SecDefaultAction with the pass action. This allows the client request to pass. To test ModSecurity, Listing 1 gives you an overview of the basic configuration discussed thus far.

Practice Session

After restarting Apache, a request like http://www.example.com/index.html?file=/etc/passwd would trigger the sample rule in line 8. Then the action defined in line 9 would block the request. The client sees an HTTP 403 Forbidden error. At the same time, lines 3 through 7 tell ModSecurity to log the transaction in the auditlog and send a detailed overview of the way the client request was processed to the debuglog. This means that your Apache error_log file will contain a note to the effect that a client request was effectively blocked, as shown in Listing 2. Once ModSecurity is working correctly, you can start adding rules and modifying them for the web applications you want to protect.

Listing 2: Rule Match

SecAuditLogType Serial [Wed Nov 04 05:39:19 2009] [error] [client 192.168.209.1] ModSecurity: Access denied with code 403 (phase 2). Pattern match "/etc/passwd" at REQUEST_URI. [file "/usr/local/httpd-2.2.14/conf/httpd.conf"] [line "420"] [hostname "www.example.com"] [uri "/index.html"] [unique_id "SvFZ138AAQEAAAc4AgQAAAAA"]

The Art of Detecting Attacks

To provide effective protection against a huge assortment of attacks, system administrators need to set up a robust ruleset for ModSecurity. To formulate rules that protect you against SQL injection, cross-site scripting, or local and remote file inclusion attacks, for example, you need in-depth knowledge of how attacks on web servers work.

Of course, not every administrator has this knowledge or the time to re-invent the wheel. To address this, the Open Web Application Security Project (OWASP) offers a predefined ruleset for ModSecurity [8] that relies on anomaly detection to protect web servers against a number of standard attacks, such as invalid client requests, SQL injection, cross-site scripting, and email or command injection. This gives you basic, fairly robust protection, which you can then modify to match your own application environment as needed.

To install these core rules, download the package and unpack it in your Apache's conf configuration directory. Then, move the rules, including the base_rules subdirectory, to the ModSecurity directory. You can look at the configuration in the modsecurity_crs_10_global_config.conf and modsecurity_crs_10_config.conf files and modify it to suit your needs. The rules are well documented. Also, you might want to enable audit and debug logging to see exactly what the module is doing. To do so, you need to include the core rules in httpd.conf as follows:

Include conf/modsecurity/*.conf
Include conf/modsecurity/ base_rules/*.conf

After restarting, your Apache web server will have a solid suit of armor that responds with an HTTP 403 to any client requests classified as attacks.

A word of caution: Web masters should first test the core rules extensively in a lab environment before letting them loose on a production system. Otherwise, the danger of preventing legitimate user access is possible. Also, remember that ModSecurity will mean about a 5 percent performance overhead for your web server.

Preventing Attacks on the United Nations

In some situations, you can't fix a web application vulnerability immediately. Imagine a major online store discovering a security hole a week before Christmas and needing several days to fix the problem, meaning the shop would be offline for that time. The owner has to make a decision: Live with the risk and keep the shop, including the vulnerability, online so you can benefit from lucrative pre-Christmas shopping, or protect the company and its customers by taking the website down and fixing the vulnerability.

ModSecurity offers a technical workaround in the form of virtual patching that allows you to define one or multiple rules that prevent the vulnerability from being exploited without actually removing it.

The ModSecurity documentation refers to a case that dates back to 2007, when attackers were trying to hack the United Nations website [9]. The sub-page with talks by Secretary-General Ban Ki-moon [10] had a statID parameter that exposed an SQL injection vulnerability (Figure 2).

The website of UN Secretary-General Ban Ki-moon was affected by an input parameter variable validation vulnerability. A ModSecurity virtual patching rule protected the website temporarily until the UN system administrator fixed the problem.
Figure 2: The website of UN Secretary-General Ban Ki-moon was affected by an input parameter variable validation vulnerability. A ModSecurity virtual patching rule protected the website temporarily until the UN system administrator fixed the problem.

If you discover a vulnerability of this kind, you can temporarily define a rule for ModSecurity that will prevent hackers from exploiting the vulnerability even though it still exists. Although this isn't a good long-term solution, it does prevent a disaster until the web developers can remove the vulnerability from the source code on the page.

The following solution would work for the UN bug:

<Location /apps/news/infocus/sgspeeches/statments_full.asp>
  SecRule &ARGS "!@eq 1"
  SecRule ARGS_NAMES "!^statid$"
  SecRule ARGS:statID "!^\d{1,3}$"
</Location>

Three lines embedded in an Apache location container state that valid user requests for the statements_full.asp file are only allowed to have one argument (first rule) called statid (second rule) with numbers of one to three digits (third rule) as their parameters. Any requests that do not follow this pattern are cleaned up by the default action, as defined in SecDefaultAction. This would effectively prevent an attacker exploiting the SQL injection vulnerability.

No Inside Information

ModSecurity also filters outgoing data, especially server responses to incoming requests. The PHP programming language throws error messages such as this:

Fatal error: Connecting to MySQL server
'dbserv.example.com' failed

Although you can disable PHP error messages in responses, Google still lists a bunch of websites where PHP error messages reveal many juicy details of applications. This information is very useful to an attacker, because it can help them understand the internal workings and structure of a web application and attack it in a more targeted way. To tell ModSecurity to catch PHP error messages and prevent them from being sent to users, you can define a rule like this:

SecRule RESPONSE_BODY "Fatal error:"

RESPONSE_BODY refers to the content of the server response to the client, and although it is not particularly elegant, it does indicate what potential you have. With a carefully crafted regular expression, you can use the same technique to prevent credit card numbers from being revealed by, for example, a compromised application in the aftermath of a successful SQL injection attack.

The Chinese Wall in Reverse

Another advanced scenario for ModSecurity involves cooperating with the GeoIP provider, Maxmind. GeoIP locates users geographically on the basis of their IP address, which means you can restrict access to a website to a specific region, such as Pennsylvania – if you have a site in Pennsylvanian Dutch that nobody else would understand – or block a country entirely. To do this, you would install the mod_geoip2 module on Apache 2, along with the GeoIP software and GeoLiteCity.dat geographical database [11].

Imagine a mechanical engineering company in Germany's Swabian region that is afraid of industrial espionage from the Far East; in this case, they could use the configuration in Listing 3 to prevent access from China – if the people in China didn't spoof their origins. The last two lines form a filter rule chain. Line 6 locates the geographical region for the requesting IP address, then line 7 dumps the request and a message into the logfile if the request comes from China. This might not be politically correct, but it is technically effective.

Listing 3: GeoIP Access

01 LoadModule geoip_module modules/mod_geoip.so
02 LoadModule security2_module modules/mod_security2.so
03 GeoIPEnable On
04 GeoIPDBFile /usr/tmp/GeoLiteCity.dat
05 SecRuleEngine On
06 SecGeoLookupDb /usr/tmp/GeoLiteCity.dat
07 SecRule REMOTE_ADDR "@geoLookup" "chain,drop,msg:'Connection attempt from .CN!'"
08 SecRule GEO:COUNTRY_CODE "@streq CN" "t:none"

Full Insurance Coverage

ModSecurity has an enormous feature scope, and it can take some time to understand it completely. But if you go to the trouble to plum the depths of the module, it will pay dividends with comprehensive methods that give you additional protection against attacks on web applications.

Thankfully, the prebuilt rulesets make it easier to get started. And, the vendor behind the project offers commercial products and services such as training. Armed with ModSecurity, administrators can sit up tall in their saddles, even if attackers are trying to make their horses bolt.