Mod_Security .htaccess tricks

.With over 70% of all attacks now carried out over the web application level, organizations need as much help as they can get in making their systems secure. WAFs are deployed to establish an external security layer that increases security, detects, and prevents attacks before they reach web applications.

Target Audience:

  • Web Server Administrators
  • Web security Adminis
  • Security consultants and other ballers.
  • Web Developers

Laying the smack down on attacks

mod_Security sits in front of ApacheModSecurity is an Apache Module just like mod_rewrite that is in fact a Web Application Firewall, providing access to every tiny bit of a HTTP Connection. HTTP Headers, Cookie and Post Payloads in their entirety, XML-RPC calls from Ajax, protocol and connection information, etc... its totally stacked.

Mod_Security uses Regex and .htaccess / httpd.conf directives similar to mod_rewrite, allowing for complete control from within .htaccess files and httpd.conf blocks.

mod_security + mod_rewrite

mod_security is the missing piece if all you know is mod_rewrite. This gives you the ability to scan ALL messages received by your website, including POST, Trackbacks, Pings, Ajax XMLHTTP calls, etc. It lets you create your own rules so that you can stop spam and prevent web application, protocol, and server attacks.

Mod_Security has the ability to parse entire POST_PAYLOADS, specific and individual POST/GET arguments... This is a spammers worst nightmare, and I'm going to make that nightmare reality for them.

Block Spam by examining POST form fields

mod_security gives you the option to block, redirect, handle using an errordocument, PAUSE, close, and chain connections. If someone is spamming your blog from many different IP's, but they often use the same keywords in a certain field of your form (like .blackjack. in the url field) mod_security lets you examine that specific url field and block all connections that contain the regexp pattern of your choice.

Enable mod_security - DreamHost

To enable mod_security, login to the DreamHost panel and navigate to the "Manage Domains" area, Edit your site and enable the extra security option.

Mod_Security Step 2

DreamHost, has set itself apart as being the top web host IMHO. They've provided the option to enable an Apache module called mod_security for any of your hosted domains. The sysops and tech over there are really doing a great job of staying true to the industry, they are web hosts, not some corporate outsourced bought-out thing. Anyways thanks DH!

Disabling mod_security conditionally per IP

This will make sure that you aren't processed by mod_security, but this only works if you have a static IP (Get your IP information). Just add this towards the top of your .htaccess file. before your mod_security code. Setting this variable causes the module to be disabled for this specific IP address, this means you won't run into any problems while posting yourself..

SetEnvIfNoCase Remote_Addr ^$ MODSEC_ENABLE=Off

# You can use multiple SetEnvIf directives to control it further.
# This only turns it off for your IP + a POST request method.
# SetEnvIf Remote_Addr ^$ MODSEC_ENABLE=Off
# SetEnvIf Request_Method !^POST$ MODSEC_ENABLE=On

Disabling mod_security with .htaccess Authorization

So I did some experimenting to see if there was an alternative way to disable mod_security for the users out there without a static IP address. I found a couple silly solutions that suggested you simply modify your browsers User Agent request header, but thats not very safe is it? No. So I came up with using Basic Authorization.

First you need to setup password protection for the directory that you want mod_security disabled for. In the case of WordPress blogs, you can use the AskApache Password Protect Plugin to get setup. Or you can generate a new htpasswd.

Adding password protection for mod_security bypass

AuthName "htaccess password prompt"
AuthType Basic
AuthUserFile /fullpath-to/.htpasswd
Require valid-user

Magic Authorization shut-off using .htaccess

When you login using this authentication, the environment variables REMOTE_USER and AUTH_TYPE are set (though its hard to find them). Since these are unique to you specifically and hard to spoof, use them to turn off mod_security only after you login. This is added up at the top of your mod_security rules.

SecFilterSelective REMOTE_USER "^yourusername$" "allow"

# More variables you can experiment with
# HTTP_Authorization|AUTH_TYPE
# You may have to add this to your mod_rewrite code
#RewriteRule .* - [E=REMOTE_USER:%{HTTP:Authorization}]

MOD_SECURITY config for DreamHost

Custom mod_security .htaccess code

Heres how I start my mod_security code, keep in mind I have a lot to learn.

# Turn the filtering engine On or Off or DynamicOnly for cgi/php/etc
SecFilterEngine On

# Only log suspicious requests
SecAuditEngine RelevantOnly

# Goes up to 9 but at 2 its overwhelming trust me
SecFilterDebugLevel 0

# Make sure that URL encoding is valid
SecFilterCheckURLEncoding On

# Unicode encoding check
SecFilterCheckUnicodeEncoding Off

# Should mod_security inspect POST payloads
SecFilterScanPOST On

# The default rule to apply to inherited rules
SecFilterDefaultAction "deny,log,status:500"

Minimal httpd.conf or .htaccess sample from source

# Enable ModSecurity
SecFilterEngine On

# Reject requests with status 403
SecFilterDefaultAction "deny,log,status:403"

# Some sane defaults
SecFilterScanPOST On
SecFilterCheckURLEncoding On
SecFilterCheckUnicodeEncoding Off

# Accept almost all byte values
SecFilterForceByteRange 1 255

# Server masking is optional
# SecServerSignature "Microsoft-IIS/5.0"

# Designate a directory for temporary files
# storage. It is a good idea to change the
# value below to a private directory, just as
# an additional measure against race conditions
SecUploadDir /tmp
SecUploadKeepFiles Off

# Only record the interesting stuff
SecAuditEngine RelevantOnly
# Uncomment below to record responses with unusual statuses
# SecAuditLogRelevantStatus ^5
SecAuditLog logs/modsec_audit.log

# You normally won't need debug logging
SecFilterDebugLevel 0
SecFilterDebugLog logs/modsec_debug.log

# Only accept request encodings we know how to handle
# we exclude GET requests from this because some (automated)
# clients supply "text/html" as Content-Type
SecFilterSelective REQUEST_METHOD "!^(GET|HEAD)$" chain
SecFilterSelective HTTP_Content-Type "!(^application/x-www-form-urlencoded$|^multipart/form-data;)"

# Do not accept GET or HEAD requests with bodies
SecFilterSelective REQUEST_METHOD "^(GET|HEAD)$" chain
SecFilterSelective HTTP_Content-Length "!^$"

# Require Content-Length to be provided with
# every POST request
SecFilterSelective REQUEST_METHOD "^POST$" chain
SecFilterSelective HTTP_Content-Length "^$"

# Don't accept transfer encodings we know we don't handle
SecFilterSelective HTTP_Transfer-Encoding "!^$"

Block WordPress Spam Forever!

Ok this is my first attempt at this, and I am really excited about the possibilities! This example goes inside your mod_security block towards the bottom, and it sets up a default action for each of the filters below it. This denies the connection, doesn't log it, and issues a 403 Forbidden Status code, which causes my Apache ErrorDocument to be displayed. This ErrorDocument can be a blank page to minimize bandwidth, or it can be a cgi perl type of script that send you an email, whatever. Many people use different Status Codes for different situations, some like 400, 412, 406, and 410 for spammers. Others prefer 503. You can see all 57 that are available for anyone running Apache and choose your own.


  • The FilesMatch Directive specifies that these rules only apply to these files.
  • The ARG_url line says that if those words appear in the form field with the id/name of "url", than deny them.
  • The ARG_comment_post_ID line denies requests that have an empty comment_post_ID field, which a surprisingly large number of spammers forget to add.
  • The final multi-line section searches ALL get and post items for any of these keywords.

Force Any Connections to be Paused a set number of ms

This is just an example to show you how cool this module is, you really shouldn't every do this except for very specific instances, because it will end up consuming your servers resources and making a ddos attack more likely. This example forces anyone who doesn't come from the site to have their connection delayed 5000 ms, then processing continues.

SecFilterSelective "HTTP_REFERER" "" log,pass,pause:5000

Only Allow Certain REQUEST_METHODS

I've created a free online scanner that you can use to scan your site and see how it handles all 27 REQUEST_METHODS.

# Sends matching requests a 405 Method Not Allowed Status Code
SecFilterSelective REQUEST_METHOD "!^(GET|HEAD|POST|OPTIONS)$" "deny,auditlog,status:405"

Debugging and Logging

First its my best guess that DreamHost is running ModSecurity v1.9.4 so its pretty darn difficult to find information about this modules directives and how to use it. So debugging through the use of trial and error and logging is the best or only way to figure it out.

On DreamHost we lucked out

If you have already been using DreamHosts "extra security" option and use your shell or check your error logs, you will have seen plenty of verbose messages about something or other being blocked. Thats mod_security doing its thing but it takes up resources on everyones servers and makes your error log close to unusable.

Control mod_security logging to your error log

Each filter can have its own actions, so turn logging on (log, auditlog) only for those you want, turn off (nolog, noauditlog) for anything else.

# Not logged
SecFilterDefaultAction "deny,nolog,noauditlog,status:500"

# Logged but not as verbose.
SecFilterDefaultAction "deny,nolog,auditlog,status:500"
Indicates that a successful match of the rule needs to be logged.
Indicates that a successful match of the rule should not be used as criteria whether the transaction should be logged to the audit log.
Prevents rule matches from appearing in both the error and audit logs.

Turn Off/On Logging JUST for your IP Address

This is handy when you want to test your rules.

# Turn logging of for your IP
SecFilterSelective REMOTE_ADDR "" "nolog,noauditlog,pass"

# Turn logging on just for your IP
SecFilterSelective REMOTE_ADDR "!^" "nolog,noauditlog,pass"
SecFilterSelective REMOTE_ADDR "" "log,auditlog,pass"

Mod_Security Directives for DreamHost

The filtering expression
The filename of the filter debugging log file
The level of the debugging log file verbosity
The variable representing areas where filtering is wanted, the filtering regular expression and optional action to take on match
On, Off, or DynamicOnly to determine when will request be filtered
On or Off to set whether the mod_security token will appear in the server signature
On or Off to set whether a request body will be processed
The default action to take on rule match
Base action template for signatures that follow this directive
On or Off to set whether rules from the parent context will be inherited
On, Off, RelevantOnly or DynamicOrRelevent to determine the level of audit logging
The filename of the audit log file
The path to the directory where uploaded files should be stored
On or Off to choose whether to keep the uploaded files or not
The path to the script that will be called to approve every uploaded file
On or Off to set whether URL encoding validation will be performed
On or Off to set whether Unicode encoding validation will be performed
The first and the last byte value of the range that will be accepted
The path of the directory to which server will be chrooted
The filename of the lock file used during the chroot process, defaults to "logs/modsec_chroot.lock"
The new signature of the server
On or Off to determine whether cookie values will be normalized for testing, defaults to On
On or Off to determine whether cookie format will be checked. Defaults to On
version of the Cookie specification to use for parsing. Possible values are 0 and 1.
Configures the charset
imports a rule from the parent configuration context.
removes a rule that was inherited from the parent configuration context.
when this directive is set to On then the rules in the parent context cannot be removed from a child context.
whether to use the old audit log format (Serial) or new (Concurrent)
path to the audit log storage area; absolute, or relative to the root of the server
list of audit log parts that go into the log.
regular expression that will be used to determine if the response status is relevant for audit logging
whether to allow rules to override SecFiltersDefaultAction configuration

Just remember each version is different, I think most of the directives in the above list are allowed on DreamHosts install, but I'm not 100%, I'm sure they'll be upgrading soon anyway.

How I got Started with mod_sec

Subject: mod_security denying blog post

Is there any way you could modify the mod_security rules for me? For the past 2-4 months I have been having a problem on WordPress where I go to edit a post and when I hit submit and POST the changes it gives me a 503 Message, which is odd to use a 503 Service Temporarily Unavailable, that threw me off for at a month thinking the service was unavailable.

Basically this only happens to about 5 of my posts, and almost all of them have something to do with php code, or some other type of programming language. An example is when I try to edit custom-phpini-tips-and-tricks. So what I've had to do is go into phpmyadmin and manually edit the database.. I don't want to turn off the mod_security for my site because I am getting hit all the time by malicious looking bots, is that the only way around this? I have no clue, but if it helps that IP is static for me, and this problem only occurs when posting to the /wp-admin/post.php file.

Reply from DreamHost Support
Unfortunately, the mod_security rules need to be changed on all machines and Apache instances if they are changed on just one as our admins like to keep the servers in sync. So getting mod_security modified isn't really something that can be done easily. I'll put in a suggestion as we do host a lot of WordPress blogs, but I can't guarantee that we'll be able to do it very quickly. I know you don't want to turn mod_security off, but honestly it's your best bet if you want to be able to post without 503's right now. If "" is always your IP, might I suggest setting up an .htaccess rule in your wp-admin folder to deny all traffic to that folder except for your IP address? I know that doesn't work if you're on the road - although adding IPs to allow to an .htaccess file is pretty easy to do - but it will keep the malicious folks out.

Example httpd.conf mod_security rules

Reprint: The old modsecurity malware-blacklist-high.txt file contained on this site used to list as a malware site, this site is not a malware site.

Some of the attacks this Apache module has the ability to smack with its unique positioning within Apache HTTP.

  1. HTTP protection - detecting violations of the HTTP protocol and a locally defined usage policy
    • SQL Injection
    • Cross-Site Scripting (XSS)
    • OS Command execution
    • Remote code inclusion
    • LDAP Injection
    • SSI Injection
    • Information leak
    • Buffer overflows
    • File disclosure
  2. Common Web Attacks Protection - detecting common web application security attack
  3. Automation detection - Detecting bots, crawlers, scanners and other surface malicious activity
  4. Trojan Protection - Detecting access to Trojans horses
  5. Errors Hiding - Disguising error messages sent by the server

mod_security links

  1. DreamHost Version 1.9.5 Manual
  2. Reference of Actions, Commands, etc. - Official Site
  3. version 1.9 got_root rules
  4. mod-security-users - mailing list
  5. mod_security online rules generator - Noel Jackson
  6. An Intro to mod_security - Atomic Playboy
  7. Get mod_sec training

MOD_SECURITY: The Most Powerful Server-Side Security Technology I've Seen

htaccess Guide Sections

Htaccess Htaccess mod_security security