You get a bonus - 1 coin for daily activity. Now you have 1 coin

Installing and Configuring mod_security on Apache in Debian and Ubuntu

Practice



mod_security is a free web application firewall (WAF) for Apache, Nginx, and IIS that provides a flexible rule system for performing operations of varying complexity. In addition, mod_security ships with a base set of filtering rules called the Core Rule Set (or CRS), which includes rules to protect against SQL injection, cross-site scripting, trojans, bots, session hijacking, and many other attacks and exploits. In Apache, mod_security is one of the additional modules, which makes it easy to install and configure.

Note: to follow this guide you first need to have a LAMP stack installed.

Installing mod_security

ModSecurity can be downloaded from the standard Debian/Ubuntu repositories:

apt-get install libapache2-modsecurity

Make sure mod_security has been loaded:

apachectl -M | grep --color security

If a module named security2_module (shared) appears on screen, everything went fine.

The ModSecurity installation includes a configuration file that needs to be renamed:

mv /etc/modsecurity/modsecurity.conf{-recommended,}

Then restart Apache:

service apache2 reload

In the Apache log directory you can find a new log file for mod_security.

root@server:~# ls -l /var/log/apache2/modsec_audit.log
-rw-r----- 1 root root 0 Oct 19 08:08 /var/log/apache2/modsec_audit.log

Configuring mod_security

For mod_security to work correctly, the out-of-the-box installation needs some additional configuration. The default configuration file is set to DetectionOnly, meaning the firewall only logs activity without blocking anything. To change this behavior, edit the modsecurity.conf file:

nano /etc/modsecurity/modsecurity.conf

Find the following line in the file:

SecRuleEngine DetectionOnly

And change it to:

SecRuleEngine On

Note: when configuring mod_security on a production server, it is recommended to change this directive only after testing all the installed rules.

The next directive that needs editing is SecResponseBodyAccess. It controls buffering of the response body; it's recommended to enable it only if you need to detect and prevent data leaks. Enabling this directive (SecResponseBodyAccess On) will not only use more server resources but will also increase the size of the log file, so it's best to disable it. To do this, find:

SecResponseBodyAccess On

And change the value:

SecResponseBodyAccess Off

Now you need to limit the maximum amount of data that can be sent to the web application. Two directives are responsible for this:

SecRequestBodyLimit
SecRequestBodyNoFilesLimit

The SecRequestBodyLimit directive sets the maximum size of POST data. If a client sends more data, the server will return a 413 Request Entity Too Large error. If the web application doesn't have a file upload mechanism, this value can be significantly reduced. The configuration file sets the following by default:

SecRequestBodyLimit 13107200

Which equals 12.5 megabytes.

The SecRequestBodyNoFilesLimit directive works similarly. The only difference is that this directive limits the size of POST data excluding the size of files.

Note: it is recommended to set this value according to the ALARP principle ("as low as reasonably practicable", i.e., based on an assessment of risk and the resources involved).

By default, the configuration file has:

SecRequestBodyNoFilesLimit 131072

Which equals 128KB.

This file also contains another parameter that affects server performance — SecRequestBodyInMemoryLimit. This parameter sets the size of the response body data that will be placed in RAM; the rest of the data will be sent to the hard drive (as swap). Since servers typically run on SSDs, this isn't a problem; however, to save RAM, the value can be reduced.

SecRequestBodyInMemoryLimit 131072

This is the default value (128KB) in the configuration file.

Testing for SQL injection

Before defining the firewall policy using rules, you need to create a PHP script vulnerable to SQL injection. Note: this is a basic PHP login script without session handling. Don't forget to replace the MySQL password in the script below with your own password.

/var/www/login.php
<html>
<body>
<?php
if(isset($_POST['login']))
{
$username = $_POST['username'];
$password = $_POST['password'];
$con = mysqli_connect('localhost','root','password','sample');
$result = mysqli_query($con, "SELECT * FROM `users` WHERE username='$username' AND password='$password'");
if(mysqli_num_rows($result) == 0)
echo 'Invalid username or password';
else
echo '<h1>Logged in</h1><p>A Secret for you....</p>';
}
else
{
?>
<form action="" method="post">
Username: <input type="text" name="username"/><br />
Password: <input type="password" name="password"/><br />
<input type="submit" name="login" value="Login"/>
</form>
<?php
}
?>
</body>
</html>

This script will display a login form. After entering the correct credentials, the message "A Secret for you" will appear on screen.

You need to add credentials to the database; to do this, create a MySQL database and table:

mysql -u root -p

This will open the mysql> command prompt.

create database sample;
connect sample;
create table users(username VARCHAR(100),password VARCHAR(100));
insert into users values('jesin','pwd');
insert into users values('alice','secret');
quit;

Open a browser and go to http://yourwebsite.com/login.php, then enter the correct login and password.

Username: User
Password: Pass

You will see a message indicating a successful login. Go back and try entering deliberately incorrect data — you'll see the message "Invalid username or password".

As you can see, the script works as intended. Now you need to perform a test SQL injection, attempting to bypass the credential check. In the Username field, enter:

' or true --

Note: a space is required after the dashes, otherwise the injection won't work.

Leave the Password field empty and click the login button.

A message for authorized users appeared on screen — meaning the injection succeeded.

To protect the server from such attacks, you need to configure the firewall rules.

Configuring mod_security rules

By default, mod_security ships with a base set of CRS (Core Rule Set) rules, located at:

root@server:~# ls -l /usr/share/modsecurity-crs/
total 40
drwxr-xr-x 2 root root 4096 Oct 20 09:45 activated_rules
drwxr-xr-x 2 root root 4096 Oct 20 09:45 base_rules
drwxr-xr-x 2 root root 4096 Oct 20 09:45 experimental_rules
drwxr-xr-x 2 root root 4096 Oct 20 09:45 lua
-rw-r--r-- 1 root root 13544 Jul 2 2012 modsecurity_crs_10_setup.conf
drwxr-xr-x 2 root root 4096 Oct 20 09:45 optional_rules
drwxr-xr-x 3 root root 4096 Oct 20 09:45 util

Documentation can be found at:

root@server:~# ls -l /usr/share/doc/modsecurity-crs/
total 40
-rw-r--r-- 1 root root 469 Jul 2 2012 changelog.Debian.gz
-rw-r--r-- 1 root root 12387 Jun 18 2012 changelog.gz
-rw-r--r-- 1 root root 1297 Jul 2 2012 copyright
drwxr-xr-x 3 root root 4096 Oct 20 09:45 examples
-rw-r--r-- 1 root root 1138 Mar 16 2012 README.Debian
-rw-r--r-- 1 root root 6495 Mar 16 2012 README.gz

To load these ready-made rules, the Apache web server needs to read the directories listed above. To do this, edit the mod-security.conf file:

nano /etc/apache2/mods-enabled/mod-security.conf

Inside <IfModule security2_module> </IfModule> add the following parameters:

Include "/usr/share/modsecurity-crs/*.conf"
Include "/usr/share/modsecurity-crs/activated_rules/*.conf"

The activated_rules directory is similar to Apache's mods-enabled directory. Rules are available in the following directories:

/usr/share/modsecurity-crs/base_rules
/usr/share/modsecurity-crs/optional_rules
/usr/share/modsecurity-crs/experimental_rules

To activate rules, you need to create symbolic links in the activated_rules directory. Create a rule to protect against SQL injection.

cd /usr/share/modsecurity-crs/activated_rules/
ln -s /usr/share/modsecurity-crs/base_rules/modsecurity_crs_41_sql_injection_attacks.conf .

For the new rules to take effect, restart Apache.

service apache2 reload

Now go back to the login page you created earlier and try performing the test SQL injection again. If the SecRuleEngine directive was enabled, you'll see a 403 Forbidden error message. If the DetectionOnly value wasn't changed, the injection will succeed, but it will be logged in the modsec_audit.log file.

Creating your own mod_security rules

This section covers creating rule chains.

First, create a rule that blocks HTML requests containing various spam words. To do this, you need to create a PHP script that displays the data entered in a text field.

/var/www/form.php
<html>
<body>
<?php
if(isset($_POST['data']))
echo $_POST['data'];
else
{
?>
<form method="post" action="">
Enter something here:<textarea name="data"></textarea>
<input type="submit"/>
</form>
<?php
}
?>
</body>
</html>

Custom rules can be added to any module configuration file or placed in the mod_security directories. Create a new file for the rules.

nano /etc/modsecurity/modsecurity_custom_rules.conf

Add the following code to this file:

SecRule REQUEST_FILENAME "form.php" "id:'400001',chain,deny,log,msg:'Spam detected'"
SecRule REQUEST_METHOD "POST" chain
SecRule REQUEST_BODY "@rx (?i:(pills|insurance|rolex))"

Save the file and restart Apache.

Open http://yourwebsite.com/form.php in your browser and enter any of these words: pills, insurance, rolex.

You'll get either a 403 error page along with a log entry, or just a log entry (this depends on the SecRuleEngine setting).

The SecRule syntax looks like this:

SecRule VARIABLES OPERATOR [ACTIONS]

In this case, the chain action (i.e., [ACTIONS]) is used to match against the REQUEST_FILENAME and form.php variables, REQUEST_METHOD and POST, and REQUEST_BODY against the string "@rx (?i:(pills|insurance|rolex))". The ?i: combination is used for case-insensitive matching. If a match is found, the deny and log actions are executed, and the message "Spam detected" appears on screen. The chain action means "AND", meaning all three listed rules must match simultaneously.

Excluding hosts and directories from the mod_security policy

Sometimes it's necessary to disable the firewall for certain directories or domains (for example, mod_security blocks SQL queries to phpMyAdmin).

Note: it's recommended to exclude CMS application back-ends.

To disable mod_security for an entire virtual host, add the following inside the <VirtualHost> section:

<IfModule security2_module>
SecRuleEngine Off
</IfModule>

To disable mod_security for a specific directory:

<Directory "/var/www/wp-admin">
<IfModule security2_module>
SecRuleEngine Off
</IfModule>
</Directory>

To avoid disabling mod_security entirely, use the SecRuleRemoveById parameter to exclude a specific rule or rule chain by specifying its ID.

<LocationMatch "/wp-admin/update.php">
<IfModule security2_module>
SecRuleRemoveById 981173
</IfModule>
</LocationMatch>

For more detailed information on how mod_security works, refer to the firewall's official documentation.

Comments

To leave a comment

If you have any suggestion, idea, thanks or comment, feel free to write. We really value feedback and are glad to hear your opinion.
To reply

Lectures and tutorial on "Running server side scripts using PHP as an example (LAMP)"

Terms: Running server side scripts using PHP as an example (LAMP)