Apache’s mod_security is a system that scans URL data and data POSTed to the server in forms for malicious attacks such as XSS, SQL injections, etc. It comes with a default ruleset that always trips up a number of standard AJAX functions in applications like WordPress and Joomla, necessitating that rules be removed from the mod_security configuration to allow the site to function properly. The method of removing these rules under Apache 2 is by whitelisting the individual rules in the VHost entry for the domain. Here’s how to do it in cPanel.
Is mod_security enabled?
root@server [~]# httpd -M | grep sec
security2_module (shared)
Yep.
A typical mod_security error message
[Sun Jul 29 18:29:47 2012] [error] [client 1.2.3.4] ModSecurity: Access denied with code 403 (phase 2). Match of "rx (?:/event\\\\.ng/|horde/services/go\\\\.php|tiki-view_cache\\\\.php|^/\\\\?out=http://|homecounter\\\\.php\\\\?offerid=.*ureferrer=http|__utm\\\\.gif\\\\?|/plugins/wpeditimage/editimage\\\\.html|/spc\\\\.php)" against "REQUEST_URI" required. [file "/usr/local/apache/conf/modsec/50_asl_rootkits.conf"] [line "53"] [id "390145"] [rev "10"] [msg "Atomicorp.com UNSUPPORTED DELAYED Rules: Rootkit attack: Generic Attempt to install rootkit"] [data "=http://www.hackers-web-site.com/what/e.txt?"] [severity "CRITICAL"] [hostname "www.domain.com"] [uri "/index.php"] [unique_id "UBVB02B-nBoACs9n0p4AAAAH"]
The error log for mod_security is /usr/local/apache/logs/error_log. There are 2 important pieces of information contained in this error:
-
[id "390145"]
This is the mod_security rule ID number. We will use this to tell mod_security which rule we want to whitelist. [hostname "www.domain.com"]
This is the name of the VirtualHost under which the error was generated. This is important because whitelisting a mod_security rule for a domain doesn’t whitelist it for the whole account; subdomains, addon domains, etc all have separate whitelists.
Creating the userdata includes
mkdir -p /usr/local/apache/conf/userdata/std/2/user/domain.com
root@server [~]# mkdir -p /usr/local/apache/conf/userdata/std/2/user/domain.com
In the above example, we are setting up an Inside VHost include. The italic std indicates this is being done in a port 80 (standard/non-secure) VirtualHost, the bold user is the cPanel username of the account, and the bold and italic domain.com is the hostname from the error message above (we discard www because that’s just a ServerAlias under the same VHost). Once we create the userdata include directory, it’s time to set up the configuration file to include. We’ll use nano because it’s the best text editor.
nano -w /usr/local/apache/conf/userdata/std/2/user/domain.com/domain.com.conf
root@server [~]# /usr/local/apache/conf/userdata/std/2/user/domain.com/domain.com.conf
You can name the file anything as long as it ends in .conf; I prefer to use domain.com.conf for clarity and simplicity. Now let’s whitelist some mod_security rules.
SecRuleRemoveById 390145
Notice that we used 390145 from the error message above. If this site was generating errors from multiple IDs, we’d put in multiple SecRuleRemoveById lines.
Enabling the userdata includes in Apache conf
/scripts/ensure_vhost_includes --user=user
This uncomments a line in the VirtualHost entry in httpd.conf for this domain to include any .conf files in /usr/local/apache/conf/userdata/std/2/user/domain.com/
/usr/local/cpanel/bin/apache_conf_distiller --update
/usr/local/cpanel/bin/build_apache_conf
These compile and distill the changes into Apache’s configuration.
Testing
The first thing to check is to ensure that the following line in the VirtualHost entry for domain.com is uncommented:
Include "/usr/local/apache/conf/userdata/std/2/user/domain.com/*.conf"
The only other way to test this is to reproduce the issue and tail the Apache error log. Make sure you have reproduction instructions available; many scripts will trigger more than one SecRule, but you’ll only see the first one due to the request being blocked.