This post is inspired by the true events of March 26, 2009 as a fellow web developer and I spent the better part of the day working out why the web server his installation of WordPress lives on was returning a 500 Internal Server Error whenever he tried to create a new Page by cutting and pasting HTML. It turns out a poorly formed rule in the default cPanel Mod_Security installation was throwing false positives as it matched the content of what he was posting to a rule denying a specific kind of SQL Injection Attack. But as is often the case in the behind-the-scenes world of web development one solution only leads to a new problem. This account is posted to document the problem(s) and solution(s) we worked out that day.
“/wp-admin/page.php” and the Dread 500 Server Error
If you’ve ever gotten 500 Server Errors while posting to your blog, forum, CMS, shopping cart or other web application there’s a very good chance you’ve been caught by a poorly formed rule in your server’s mod_security configuration. While a thorough accounting of mod_security is beyond the purview of this post let it suffice to say that for those who aren’t familiar with it, mod_security is a firewall for web applications (Read More: ModSecurity.org). ModSecurity is an Apache module which attempts to prevent malicious execution of code by filtering all requests to the server through its rulesets. It works by matching incoming requests against patterns designed to catch code that could compromise or otherwise harm a website or the server itself.
On closer examination of the mod_security log file you will probably find something that looks like this:
Access denied with code 500 (phase 2). Pattern match "(insert[[:space:]]+into.+values|select.*from.+[a-z|A-Z|0-9]|select.+from|bulk[[:space:]]+insert|union.+select|convert.+\\(.*from)” at ARGS:content. [id "300016"] [rev "2"] [msg "Generic SQL injection protection"] [severity "CRITICAL"]
That is the rule that is causing the false positive. You should also see a line that looks very familiar because it’s the path and parameters of the file the web application you are using is trying to POST to.
Overall, ModSecurity is an extremely effective tool when it comes to preventing SQL Injection attacks, however, every once in a while it can be the cause of extreme frustration as you helplessly try to figure out why all of a sudden you are getting a 500 Internal Server Error instead of the success screen you expect after posting your latest to WordPress, Zen-Cart, phpBB or any of the other widely available open source web applications. And because Mod_security is so ubiquitous, quietly humming away in the background, as you go about your business, you may go months or even years without running into a problem with it and by that time you of course will be completely stumped, you may even start thinking up crazy explanations for what’s causing the problem. You may be told to reinstall your cms, blog, forum, etc. Maybe there’s a corrupt file in there. Or if you’re really bright you might even start hacking the code itself trying to find the fix yourself. Please don’t do this. Unless of course you are the code’s author in which case–please do.
Even once you narrow it down to Mod_Security you may be tempted to try an .htaccess override because the web is full of examples of how to override Mod_Security per site, per file just simply by adding lines like these to your .htaccess file.
<IfModule mod_security.c>SecFilterEngine off</IfModule>
<IfModule mod_security.c>SecFilterInheritance Off</IfModule>
If you are one of the (un)fortunate ones who are unable to get things working by some variation of these tricks then you have essentially three choices:
- 1. If you are on a shared hosting account you will need to call or email your web host and ask that they add a white list rule that disables the rule that is giving the false positive. Ideally you would do this for the file in question and not just disable Mod_Security for your entire account which some people will try to tell you is the best solution. Personally, I’d run if that were the case.
- 2. If you are the webhost and you’re administering your own dedicated server or a VPS you should have the option of opening a support ticket with the company you are leasing from and one of their admins will do this for you. LiquidWeb and their Heroic Support is fantastic for things like this.
- 3. If neither of the above apply and you’re on your own, or if you just have time to burn and want to learn something about the way your webserver works you can add a rule yourself
Whitelisting WordPress with Mod_Security
While previously I stated that the false positive was caused by a poorly formed rule in Mod_Security’s configuration files, it would be equally reasonable to assume the rule was crafted by design and that the best practice for solving your Mod_Security woes and getting on with it is to create another rule specifically telling Mod_Security that it’s okay to let these requests pass unmolested. This is a fairly conservative i.e. paranoid security model but it’s tried and true: Deny All, Accept Few. So how to do this? Fortunately if you are somewhat handy with the shell you should be able to patch your Mod_Security in no time. You will need root access to your server, so if you don’t have that then see Step 1, above.
In the example that follows I assume you have root or access to sudo, can SSH into your server and can use a vi or nano to edit files and that you are running cPanel 11 and WHM. Here’s what you need to do.
- 1. Log in to your server either with the root account or with your regular user account. You can wrap your commands with sudo or escalate your privileges to root as necessary.
- 2. Change to the Mod_Security configuration directory:
- 3. Open the whitelist.conf file for editing:
- 4. Add the following lines of code to whitelist.conf
<LocationMatch "/wp-admin/post.php">SecRuleRemoveById 300015 300016 300017
SecRuleRemoveById 300015 300016 300017
SecRuleRemoveById 300015 300016 300017
- 5. Save whitelist.conf
- 6. Restart Apache
If all goes well once Apache restarts your web server should stop sending 500 Internal Server Errors when you try to post your content and instead do what it’s supposed to do and accept your post.