I've done a bunch of research and I've found a lot of pieces, but haven't been able to nail down something that will do what I need it to do. To start off, I'm on a CentOS 7 server running ModSecurity 2.7.3.
I want to be able to scan all parameters except certain ones and run them through all the standard core rules that have already been setup. For example if I have a file path/to/myfile.php
and it has 2 parameters that get sent to it param1
and param2
. When a user hits myfile.php
I want it to run all the cute checks it can against param1
and param2
except with param2
I don't need it to check WEB_ATTACK/XSS
because it should be expecting some HTML for that field (or whatever).
I don't think I can remove rules by IDs (unless my understanding is flawed). We are currently setting this up on a staging server, and then we will install ModSecurity and the exclusions we've setup on the staging server and copy them to the live servers. I don't think the rules IDs would match up from the logs on staging (again unless I'm misunderstanding something) and it wouldn't be "fun" to throw ModSecurity on our live servers and wait for rules to start tripping.
Here's what I've tried...
<LocationMatch /path/to/myfile.php>
<IfModule mod_security2.c>
SecRuleEngine Off # This is super bad I know
</IfModule>
</LocationMatch>
That's a bad idea, so I can do this...
<LocationMatch /path/to/myfile.php>
<IfModule mod_security2.c>
SecRuleRemoveByTag "WEB_ATTACK/XSS" # better but.....still not close enough
</IfModule>
</LocationMatch>
So I've tried with no success stuff like the following:
SecRule ARGS|!ARGS:param2 "@detectXSS" # only 2.8.0 and above :(
# or
SecRule ARGS|!ARGS:param2 "ctl:ruleRemoveByTag=WEB_ATTACK/XSS"
# or
SecRule REQUEST_FILENAME "@streq /path/to/myfile.php" "pass,ctl:ruleRemoveByTag=WEB_ATTACK/XSS;ARGS:param2"
The documentation for ModSecurity is.....good, but I need a little deeper understanding, esp with ctl stuff. I also looked at some other questions here and here and seen some posts around the web that helped steer me in the right direction here (very nice write up), here, here, and here
If I needed to do param2 and param3, would I have to write 2 rules or can they be combined somehow (ARGS:param2,param3
)?
Your understanding regarding ids is flawed. Rule ids should be the same on staging as on live - unless you are running different rules on them (which kind of defeats the point of a staging server then if not a true likeness of live). Mostly people download, buy or write a rule set with the OWASP Core Rule Set being a popular (and free!) rule set.
There is an almost identical example to what you want to do in the ctl documentation that you linked to but with ids:
# white-list the user parameter for rule #981260 when the REQUEST_URI is /index.php
SecRule REQUEST_URI "@beginsWith /index.php" "phase:1,t:none,pass, \
nolog,ctl:ruleRemoveTargetById=981260;ARGS:user
So for you this would become:
SecRule REQUEST_URI "@beginsWith /path/to/myfile.php" "id:1234,phase:2,t:none,pass, \
nolog,ctl:ruleRemoveTargetById=973336;ARGS:param2
Note the OWASP CRS (not sure if you are using that?) XSS checks are phase 2 checks hence why I've changed that and Ids are now mandatory as of 2.7. You could extend this to include lots of rule ids or different arguments:
SecRule REQUEST_URI "@beginsWith /path/to/myfile.php" "phase:2,t:none,pass, \
nolog,ctl:ruleRemoveTargetById=973336;ARGS:param2,\
ctl:ruleRemoveTargetById=973337;ARGS:param2,\
ctl:ruleRemoveTargetById=973338;ARGS:param2,\
ctl:ruleRemoveTargetById=973336;ARGS:param3
However, it's kind of tedious to set all the ids, so, as you wanted to do it by tag, you could try the following which should work though is untested:
SecRule REQUEST_URI "@beginsWith /path/to/myfile.php" "id:1234,phase:2,t:none,pass, \
nolog,ctl:ruleRemoveTargetByTag="OWASP_CRS/WEB_ATTACK/XSS";ARGS:param2
Note that the OWASP_CRS tag is actually "OWASP_CRS/WEB_ATTACK/XSS"
rather than just "WEB_ATTACK/XSS"
and not sure if it will match the partial bit so put the full text in, under the assumption that's the rule set you are using.
Again you could have multiple ctl
actions in this line if you did want to white list param3 similarly.
If none of that works you could use chained rules instead of the ctl action:
SecRule REQUEST_URI "@beginsWith /path/to/myfile.php" "id:1234,phase:2,t:none,pass,chain \
SecRuleUpdateTargetByTag "OWASP_CRS:WEB_ATTACK/XSS" !ARGS:param2
This allows multiple items to be checked and also gives you access to all the ModSecurity rule commands if there isn't an equivalent ctl
command (though ctl
seems to handle most things). Though those will require one chained rule per argument.
IMPORTANT NOTE: The ordering is important and confusing. SecRuleUpdateTargetByTag should be specified AFTER the rules they are altering but ctl amends need to be specified BEFORE the rules they are amending.
It should also be noted that Location and LocationMatch does not work for phase 1 rules (as these are processed before Apache runs Location and LocationMatch logic) so for that reason I prefer to use ModSecurity REQUEST_URI instead. Even for phase 2 and above rules, for consistency, though they should work in Location and LocationMatch sections.
Finally you can (and should!) put ModSecurity on your live server in DetectionOnly mode initially so it will log all the violations but not block them:
SecRuleEngine DetectionOnly
Then as you fine tune the rules you will see a drop in false positives until you are comfortable to turn it on fully.
Btw I can highly recommend the ModSecurity handbook written by the original author of ModSecurity before he moved on. Hasn't been updated since ModSecurity 2.6 but, other than id becoming mandatory, everything it covers is still relevant and will give you a good grounding in ModSecurity and then you can check out the ModSecurity release notes (either in your install or here) to see what's changed. Would also recommend you upgrade to latest version (2.9.1) as quite a few bug fixes since 2.7.3.