Difference between revisions of "Mod security"
Line 1: | Line 1: | ||
+ | * The ruleset(s) provided by modsecurity/modsececurity2 will affect NATS processes and these rules are enabled by default. These default ruleset(s) need to be disabled so they will not get used. | ||
+ | * Host can create custom rules that only apply for things outside of NATS | ||
+ | * Host's responsibility to ensure that these rules don't affect NATS functions on NATS domain & linkdomains | ||
+ | |||
+ | == General Information on modsecurity & modsececurity2 == | ||
* The ruleset(s) provided by modsecurity/modsececurity2 will affect NATS processes and these rules are enabled by default. These default ruleset(s) need to be disabled so they will not get used. | * The ruleset(s) provided by modsecurity/modsececurity2 will affect NATS processes and these rules are enabled by default. These default ruleset(s) need to be disabled so they will not get used. | ||
* Host can create custom rules that only apply for things outside of NATS | * Host can create custom rules that only apply for things outside of NATS | ||
Line 9: | Line 14: | ||
These are example rules that will rate-limit requests and prevent bot spam. | These are example rules that will rate-limit requests and prevent bot spam. | ||
You can use these rulesets as a reference, but be aware that you are responsible for your configuration and that you should NOT use the default rulesets provided by modsececurity2. | You can use these rulesets as a reference, but be aware that you are responsible for your configuration and that you should NOT use the default rulesets provided by modsececurity2. | ||
+ | |||
+ | ==Member Signup Throttling== | ||
+ | Rate-limit to 75 requests to signup/signup.php using the same password or email within a 10 minute period. | ||
+ | <pre> | ||
+ | <Location /signup/signup.php> | ||
+ | # needs to be enabled to read POST data | ||
+ | SecRequestBodyAccess On | ||
+ | |||
+ | # jump to SecMarker if signup[password] variable is not passed in request | ||
+ | SecRule ARGS:signup[password] "^$" "phase:2,id:'981041',t:none,nolog,pass,skipAfter:END_BRUTE_FORCE_PROTECTION_CHECKS1" | ||
+ | |||
+ | # set 'RESOURCE' to value of signup[password] if it is not blank | ||
+ | SecRule ARGS:signup[password] ".*" "phase:2,id:'1',chain,t:none,nolog,pass" | ||
+ | SecAction initcol:RESOURCE=%{ARGS.signup[password]} | ||
+ | |||
+ | # enforce an existing block | ||
+ | SecRule RESOURCE:bf_block "@eq 1" \ | ||
+ | "id:2,phase:2,deny,\ | ||
+ | msg:'Password \"%{ARGS.signup[password]}\" blocked because of suspected brute-force attack'" | ||
+ | |||
+ | # set or increase count of how many times a signup[password] value was used and have it expire after 600 seconds | ||
+ | SecRule ARGS:signup[password] ".*" "phase:5,id:3,t:none,nolog,pass,setvar:RESOURCE.bf_counter=+1,expirevar:RESOURCE.bf_counter=600" | ||
+ | |||
+ | # check for too many requests for a single signup[password] value and block future requests that contain that value | ||
+ | SecRule RESOURCE:bf_counter "@ge 75" \ | ||
+ | "id:4,phase:5,t:none,pass,\ | ||
+ | setvar:RESOURCE.bf_block,\ | ||
+ | setvar:!RESOURCE.bf_counter,\ | ||
+ | expirevar:RESOURCE.bf_block=600" | ||
+ | |||
+ | # jump to this point if signup[password] is not passed in request | ||
+ | SecMarker END_BRUTE_FORCE_PROTECTION_CHECKS1 | ||
+ | |||
+ | |||
+ | |||
+ | # jump to SecMarker if signup[password:1:6:16:::password_check] variable is not passed in request | ||
+ | SecRule ARGS:signup[password:1:6:16:::password_check] "^$" "phase:2,id:'981042',t:none,nolog,pass,skipAfter:END_BRUTE_FORCE_PROTECTION_CHECKS2" | ||
+ | |||
+ | |||
+ | # set 'RESOURCE' to value of signup[password:1:6:16:::password_check] if it is not blank | ||
+ | SecRule ARGS:signup[password:1:6:16:::password_check] ".*" "phase:2,id:'5',chain,t:none,nolog,pass" | ||
+ | SecAction initcol:RESOURCE=%{ARGS.signup[password:1:6:16:::password_check]} | ||
+ | |||
+ | # enforce an existing block | ||
+ | SecRule RESOURCE:bf_block "@eq 1" \ | ||
+ | "id:6,phase:2,deny,\ | ||
+ | msg:'Password \"%{ARGS.signup[password:1:6:16:::password_check]}\" blocked because of suspected brute-force attack'" | ||
+ | |||
+ | # set or increase count of how many times a signup[password:1:6:16:::password_check] value was used and have it expire after 600 seconds | ||
+ | SecRule ARGS:signup[password:1:6:16:::password_check] ".*" "phase:5,id:7,t:urlDecode,nolog,pass,setvar:RESOURCE.bf_counter=+1,expirevar:RESOURCE.bf_counter=600" | ||
+ | |||
+ | # check for too many requests for a single signup[password:1:6:16:::password_check] value and block future requests that contain that value | ||
+ | SecRule RESOURCE:bf_counter "@ge 75" \ | ||
+ | "id:8,phase:5,t:none,pass,\ | ||
+ | setvar:RESOURCE.bf_block,\ | ||
+ | setvar:!RESOURCE.bf_counter,\ | ||
+ | expirevar:RESOURCE.bf_block=600" | ||
+ | |||
+ | # jump to this point if signup[password:1:6:16:::password_check] is not passed in request | ||
+ | SecMarker END_BRUTE_FORCE_PROTECTION_CHECKS2 | ||
+ | |||
+ | |||
+ | |||
+ | # jump to SecMarker if signup[email] variable is not passed in request | ||
+ | SecRule ARGS:signup[email] "^$" "phase:2,id:'981043',t:none,nolog,pass,skipAfter:END_BRUTE_FORCE_PROTECTION_CHECKS3" | ||
+ | |||
+ | |||
+ | # set 'RESOURCE' to value of signup[email] if it is not blank | ||
+ | SecRule ARGS:signup[email] ".*" "phase:2,id:'9',chain,t:none,nolog,pass" | ||
+ | SecAction initcol:RESOURCE=%{ARGS.signup[email]} | ||
+ | |||
+ | # enforce an existing block | ||
+ | SecRule RESOURCE:bf_block "@eq 1" \ | ||
+ | "id:10,phase:2,deny,\ | ||
+ | msg:'Email \"%{ARGS.signup[email]}\" blocked because of suspected brute-force attack'" | ||
+ | |||
+ | # set or increase count of how many times a signup[email] value was used and have it expire after 600 seconds | ||
+ | SecRule ARGS:signup[email] ".*" "phase:5,id:11,t:none,nolog,pass,setvar:RESOURCE.bf_counter=+1,expirevar:RESOURCE.bf_counter=600" | ||
+ | |||
+ | # check for too many requests for a single signup[email] value and block future requests that contain that value | ||
+ | SecRule RESOURCE:bf_counter "@ge 75" \ | ||
+ | "id:12,phase:5,t:none,pass,\ | ||
+ | setvar:RESOURCE.bf_block,\ | ||
+ | setvar:!RESOURCE.bf_counter,\ | ||
+ | expirevar:RESOURCE.bf_block=600" | ||
+ | |||
+ | # jump to this point if signup[email] is not passed in request | ||
+ | SecMarker END_BRUTE_FORCE_PROTECTION_CHECKS3 | ||
+ | |||
+ | |||
+ | |||
+ | # jump to SecMarker if signup[email:1:1:128:::email_check] variable is not passed in request | ||
+ | SecRule ARGS:signup[email:1:1:128:::email_check] "^$" "phase:2,id:'981044',t:none,nolog,pass,skipAfter:END_BRUTE_FORCE_PROTECTION_CHECKS4" | ||
+ | |||
+ | # set 'RESOURCE' to value of signup[email:1:1:128:::email_check] if it is not blank | ||
+ | SecRule ARGS:signup[email:1:1:128:::email_check] ".*" "phase:2,id:'13',chain,t:none,nolog,pass" | ||
+ | SecAction initcol:RESOURCE=%{ARGS.signup[email:1:1:128:::email_check]} | ||
+ | |||
+ | # enforce an existing block | ||
+ | SecRule RESOURCE:bf_block "@eq 1" \ | ||
+ | "id:14,phase:2,deny,\ | ||
+ | msg:'Email \"%{ARGS.signup[email:1:1:128:::email_check]}\" blocked because of suspected brute-force attack'" | ||
+ | |||
+ | # set or increase count of how many times a signup[email:1:1:128:::email_check] value was used and have it expire after 600 seconds | ||
+ | SecRule ARGS:signup[email:1:1:128:::email_check] ".*" "phase:5,id:15,t:none,nolog,pass,setvar:RESOURCE.bf_counter=+1,expirevar:RESOURCE.bf_counter=600" | ||
+ | |||
+ | # check for too many requests for a single signup[email:1:1:128:::email_check] and block future requests that contain that value | ||
+ | SecRule RESOURCE:bf_counter "@ge 75" \ | ||
+ | "id:16,phase:5,t:none,pass,\ | ||
+ | setvar:RESOURCE.bf_block,\ | ||
+ | setvar:!RESOURCE.bf_counter,\ | ||
+ | expirevar:RESOURCE.bf_block=600" | ||
+ | |||
+ | # jump to this point if signup[email:1:1:128:::email_check] is not passed in request | ||
+ | SecMarker END_BRUTE_FORCE_PROTECTION_CHECKS4 | ||
+ | </Location> | ||
+ | </pre> | ||
+ | |||
+ | ==Plus script Throttling== | ||
+ | Rate-limit the *plus scripts (as well as approved,upgraded,duplicate, and submit scripts) to 75 requests from the same ip address within a 10 minute period | ||
+ | <pre> | ||
+ | |||
+ | <LocationMatch "/signup/(upgradeplus|approved|upgraded|upsellplus|packageplus|tokenplus|cancelplus|duplicate|verifyplus|signupplus|submit).php"> | ||
+ | |||
+ | SecAction initcol:IP=%{REMOTE_ADDR},pass,nolog,id:21 | ||
+ | |||
+ | # enforce an existing block | ||
+ | SecRule IP:bf_block "@eq 1" \ | ||
+ | "id:22,phase:2,deny,\ | ||
+ | msg:'IP \"%{REMOTE_ADDR}\" is being throttled'" | ||
+ | |||
+ | |||
+ | SecRule REMOTE_ADDR ".*" "phase:5,id:23,t:none,nolog,pass,setvar:IP.bf_counter=+1,expirevar:IP.bf_counter=600" | ||
+ | |||
+ | |||
+ | SecRule IP:bf_counter "@ge 75" \ | ||
+ | "id:24,phase:5,t:none,pass,\ | ||
+ | setvar:IP.bf_block,\ | ||
+ | setvar:!IP.bf_counter,\ | ||
+ | expirevar:IP.bf_block=600" | ||
+ | |||
+ | |||
+ | SecMarker END_BRUTE_FORCE_PROTECTION_CHECKS5 | ||
+ | </LocationMatch> | ||
+ | |||
+ | </pre> | ||
+ | |||
+ | |||
+ | ==Affiliate Login Throttling== | ||
+ | Rate-limit affiliate login attempts to 100, but also dynamically increase rate-limiting to 5 if there are more than 1000 login attempts within a 5 minute period | ||
+ | <pre> | ||
+ | #Bruteforce checking for affiliate login | ||
+ | #Block ip if X amount of hits to internal.php containing user and pass params | ||
+ | <Location /internal.php> | ||
+ | # needs to be enabled to read POST data | ||
+ | SecRequestBodyAccess On | ||
+ | |||
+ | # jump to SecMarker if user variable is not passed in request | ||
+ | SecRule &ARGS:user "@eq 0" "phase:2,id:'30',t:none,nolog,pass,skipAfter:END_BRUTE_FORCE_PROTECTION_CHECKS6" | ||
+ | SecRule ARGS:user "^$" "phase:2,id:'31',t:none,nolog,pass,skipAfter:END_BRUTE_FORCE_PROTECTION_CHECKS6" | ||
+ | |||
+ | # jump to SecMarker if pass variable is not passed in request | ||
+ | SecRule &ARGS:pass "@eq 0" "phase:2,id:'32',t:none,nolog,pass,skipAfter:END_BRUTE_FORCE_PROTECTION_CHECKS6" | ||
+ | SecRule ARGS:pass "^$" "phase:2,id:'33',t:none,nolog,pass,skipAfter:END_BRUTE_FORCE_PROTECTION_CHECKS6" | ||
+ | |||
+ | SecAction initcol:IP=%{REMOTE_ADDR},pass,nolog,id:34 | ||
+ | |||
+ | # enforce an existing block | ||
+ | SecRule IP:bf_block "@eq 1" \ | ||
+ | "id:35,phase:2,deny,msg:'IP \"%{REMOTE_ADDR}\" is being throttled, too many affiliate login attempts'" | ||
+ | |||
+ | # check if we're getting a ton of login attempts | ||
+ | SecAction initcol:RESOURCE=%{REQUEST_FILENAME},pass,nolog,id:39 | ||
+ | |||
+ | SecRule REQUEST_FILENAME ".*" "phase:5,id:41,t:none,nolog,pass,setvar:RESOURCE.bf_counter=+1,expirevar:RESOURCE.bf_counter=300" | ||
+ | SecRule REMOTE_ADDR ".*" "phase:5,id:36,t:none,nolog,pass,setvar:IP.bf_counter=+1,expirevar:IP.bf_counter=300" | ||
+ | |||
+ | # increase throttling if we're under attack | ||
+ | # start throttling after 5 requests | ||
+ | SecRule RESOURCE:bf_block "@eq 1" "id:38,phase:5,chain,msg:'too many login attempts occuring, increase throttling',t:none" | ||
+ | SecRule IP:bf_counter "@ge 5" "t:none,\ | ||
+ | setvar:IP.bf_block,\ | ||
+ | setvar:!IP.bf_counter,\ | ||
+ | expirevar:IP.bf_block=300" | ||
+ | |||
+ | |||
+ | # increase throttling if more than 1000 login attempts total | ||
+ | SecRule RESOURCE:bf_counter "@ge 1000" \ | ||
+ | "id:40,phase:5,t:none,pass,\ | ||
+ | setvar:RESOURCE.bf_block,\ | ||
+ | setvar:!RESOURCE.bf_counter,\ | ||
+ | expirevar:RESOURCE.bf_block=300" | ||
+ | |||
+ | |||
+ | # throttle ip after 100 requests | ||
+ | SecRule IP:bf_counter "@ge 100" \ | ||
+ | "id:37,phase:5,t:none,pass,\ | ||
+ | setvar:IP.bf_block,\ | ||
+ | setvar:!IP.bf_counter,\ | ||
+ | expirevar:IP.bf_block=300" | ||
+ | |||
+ | # jump to this point if user or pass is not passed in request | ||
+ | SecMarker END_BRUTE_FORCE_PROTECTION_CHECKS6 | ||
+ | </Location> | ||
+ | </pre> | ||
+ | |||
+ | --> | ||
==Member Signup Throttling== | ==Member Signup Throttling== |
Revision as of 13:15, 24 June 2021
- The ruleset(s) provided by modsecurity/modsececurity2 will affect NATS processes and these rules are enabled by default. These default ruleset(s) need to be disabled so they will not get used.
- Host can create custom rules that only apply for things outside of NATS
- Host's responsibility to ensure that these rules don't affect NATS functions on NATS domain & linkdomains
General Information on modsecurity & modsececurity2
- The ruleset(s) provided by modsecurity/modsececurity2 will affect NATS processes and these rules are enabled by default. These default ruleset(s) need to be disabled so they will not get used.
- Host can create custom rules that only apply for things outside of NATS
- Host's responsibility to ensure that these rules don't affect NATS functions on NATS domain & linkdomains
Example Custom Rules for ModSecurity2
https://github.com/SpiderLabs/ModSecurity/wiki/Reference-Manual-(v2.x)
You can create customized rules with modsecurity2, to rate-limit by ip, request vars being sent, etc. These are example rules that will rate-limit requests and prevent bot spam. You can use these rulesets as a reference, but be aware that you are responsible for your configuration and that you should NOT use the default rulesets provided by modsececurity2.
Member Signup Throttling
Rate-limit to 75 requests to signup/signup.php using the same password or email within a 10 minute period.
<Location /signup/signup.php> # needs to be enabled to read POST data SecRequestBodyAccess On # jump to SecMarker if signup[password] variable is not passed in request SecRule ARGS:signup[password] "^$" "phase:2,id:'981041',t:none,nolog,pass,skipAfter:END_BRUTE_FORCE_PROTECTION_CHECKS1" # set 'RESOURCE' to value of signup[password] if it is not blank SecRule ARGS:signup[password] ".*" "phase:2,id:'1',chain,t:none,nolog,pass" SecAction initcol:RESOURCE=%{ARGS.signup[password]} # enforce an existing block SecRule RESOURCE:bf_block "@eq 1" \ "id:2,phase:2,deny,\ msg:'Password \"%{ARGS.signup[password]}\" blocked because of suspected brute-force attack'" # set or increase count of how many times a signup[password] value was used and have it expire after 600 seconds SecRule ARGS:signup[password] ".*" "phase:5,id:3,t:none,nolog,pass,setvar:RESOURCE.bf_counter=+1,expirevar:RESOURCE.bf_counter=600" # check for too many requests for a single signup[password] value and block future requests that contain that value SecRule RESOURCE:bf_counter "@ge 75" \ "id:4,phase:5,t:none,pass,\ setvar:RESOURCE.bf_block,\ setvar:!RESOURCE.bf_counter,\ expirevar:RESOURCE.bf_block=600" # jump to this point if signup[password] is not passed in request SecMarker END_BRUTE_FORCE_PROTECTION_CHECKS1 # jump to SecMarker if signup[password:1:6:16:::password_check] variable is not passed in request SecRule ARGS:signup[password:1:6:16:::password_check] "^$" "phase:2,id:'981042',t:none,nolog,pass,skipAfter:END_BRUTE_FORCE_PROTECTION_CHECKS2" # set 'RESOURCE' to value of signup[password:1:6:16:::password_check] if it is not blank SecRule ARGS:signup[password:1:6:16:::password_check] ".*" "phase:2,id:'5',chain,t:none,nolog,pass" SecAction initcol:RESOURCE=%{ARGS.signup[password:1:6:16:::password_check]} # enforce an existing block SecRule RESOURCE:bf_block "@eq 1" \ "id:6,phase:2,deny,\ msg:'Password \"%{ARGS.signup[password:1:6:16:::password_check]}\" blocked because of suspected brute-force attack'" # set or increase count of how many times a signup[password:1:6:16:::password_check] value was used and have it expire after 600 seconds SecRule ARGS:signup[password:1:6:16:::password_check] ".*" "phase:5,id:7,t:urlDecode,nolog,pass,setvar:RESOURCE.bf_counter=+1,expirevar:RESOURCE.bf_counter=600" # check for too many requests for a single signup[password:1:6:16:::password_check] value and block future requests that contain that value SecRule RESOURCE:bf_counter "@ge 75" \ "id:8,phase:5,t:none,pass,\ setvar:RESOURCE.bf_block,\ setvar:!RESOURCE.bf_counter,\ expirevar:RESOURCE.bf_block=600" # jump to this point if signup[password:1:6:16:::password_check] is not passed in request SecMarker END_BRUTE_FORCE_PROTECTION_CHECKS2 # jump to SecMarker if signup[email] variable is not passed in request SecRule ARGS:signup[email] "^$" "phase:2,id:'981043',t:none,nolog,pass,skipAfter:END_BRUTE_FORCE_PROTECTION_CHECKS3" # set 'RESOURCE' to value of signup[email] if it is not blank SecRule ARGS:signup[email] ".*" "phase:2,id:'9',chain,t:none,nolog,pass" SecAction initcol:RESOURCE=%{ARGS.signup[email]} # enforce an existing block SecRule RESOURCE:bf_block "@eq 1" \ "id:10,phase:2,deny,\ msg:'Email \"%{ARGS.signup[email]}\" blocked because of suspected brute-force attack'" # set or increase count of how many times a signup[email] value was used and have it expire after 600 seconds SecRule ARGS:signup[email] ".*" "phase:5,id:11,t:none,nolog,pass,setvar:RESOURCE.bf_counter=+1,expirevar:RESOURCE.bf_counter=600" # check for too many requests for a single signup[email] value and block future requests that contain that value SecRule RESOURCE:bf_counter "@ge 75" \ "id:12,phase:5,t:none,pass,\ setvar:RESOURCE.bf_block,\ setvar:!RESOURCE.bf_counter,\ expirevar:RESOURCE.bf_block=600" # jump to this point if signup[email] is not passed in request SecMarker END_BRUTE_FORCE_PROTECTION_CHECKS3 # jump to SecMarker if signup[email:1:1:128:::email_check] variable is not passed in request SecRule ARGS:signup[email:1:1:128:::email_check] "^$" "phase:2,id:'981044',t:none,nolog,pass,skipAfter:END_BRUTE_FORCE_PROTECTION_CHECKS4" # set 'RESOURCE' to value of signup[email:1:1:128:::email_check] if it is not blank SecRule ARGS:signup[email:1:1:128:::email_check] ".*" "phase:2,id:'13',chain,t:none,nolog,pass" SecAction initcol:RESOURCE=%{ARGS.signup[email:1:1:128:::email_check]} # enforce an existing block SecRule RESOURCE:bf_block "@eq 1" \ "id:14,phase:2,deny,\ msg:'Email \"%{ARGS.signup[email:1:1:128:::email_check]}\" blocked because of suspected brute-force attack'" # set or increase count of how many times a signup[email:1:1:128:::email_check] value was used and have it expire after 600 seconds SecRule ARGS:signup[email:1:1:128:::email_check] ".*" "phase:5,id:15,t:none,nolog,pass,setvar:RESOURCE.bf_counter=+1,expirevar:RESOURCE.bf_counter=600" # check for too many requests for a single signup[email:1:1:128:::email_check] and block future requests that contain that value SecRule RESOURCE:bf_counter "@ge 75" \ "id:16,phase:5,t:none,pass,\ setvar:RESOURCE.bf_block,\ setvar:!RESOURCE.bf_counter,\ expirevar:RESOURCE.bf_block=600" # jump to this point if signup[email:1:1:128:::email_check] is not passed in request SecMarker END_BRUTE_FORCE_PROTECTION_CHECKS4 </Location>
Plus script Throttling
Rate-limit the *plus scripts (as well as approved,upgraded,duplicate, and submit scripts) to 75 requests from the same ip address within a 10 minute period
<LocationMatch "/signup/(upgradeplus|approved|upgraded|upsellplus|packageplus|tokenplus|cancelplus|duplicate|verifyplus|signupplus|submit).php"> SecAction initcol:IP=%{REMOTE_ADDR},pass,nolog,id:21 # enforce an existing block SecRule IP:bf_block "@eq 1" \ "id:22,phase:2,deny,\ msg:'IP \"%{REMOTE_ADDR}\" is being throttled'" SecRule REMOTE_ADDR ".*" "phase:5,id:23,t:none,nolog,pass,setvar:IP.bf_counter=+1,expirevar:IP.bf_counter=600" SecRule IP:bf_counter "@ge 75" \ "id:24,phase:5,t:none,pass,\ setvar:IP.bf_block,\ setvar:!IP.bf_counter,\ expirevar:IP.bf_block=600" SecMarker END_BRUTE_FORCE_PROTECTION_CHECKS5 </LocationMatch>
Affiliate Login Throttling
Rate-limit affiliate login attempts to 100, but also dynamically increase rate-limiting to 5 if there are more than 1000 login attempts within a 5 minute period
#Bruteforce checking for affiliate login #Block ip if X amount of hits to internal.php containing user and pass params <Location /internal.php> # needs to be enabled to read POST data SecRequestBodyAccess On # jump to SecMarker if user variable is not passed in request SecRule &ARGS:user "@eq 0" "phase:2,id:'30',t:none,nolog,pass,skipAfter:END_BRUTE_FORCE_PROTECTION_CHECKS6" SecRule ARGS:user "^$" "phase:2,id:'31',t:none,nolog,pass,skipAfter:END_BRUTE_FORCE_PROTECTION_CHECKS6" # jump to SecMarker if pass variable is not passed in request SecRule &ARGS:pass "@eq 0" "phase:2,id:'32',t:none,nolog,pass,skipAfter:END_BRUTE_FORCE_PROTECTION_CHECKS6" SecRule ARGS:pass "^$" "phase:2,id:'33',t:none,nolog,pass,skipAfter:END_BRUTE_FORCE_PROTECTION_CHECKS6" SecAction initcol:IP=%{REMOTE_ADDR},pass,nolog,id:34 # enforce an existing block SecRule IP:bf_block "@eq 1" \ "id:35,phase:2,deny,msg:'IP \"%{REMOTE_ADDR}\" is being throttled, too many affiliate login attempts'" # check if we're getting a ton of login attempts SecAction initcol:RESOURCE=%{REQUEST_FILENAME},pass,nolog,id:39 SecRule REQUEST_FILENAME ".*" "phase:5,id:41,t:none,nolog,pass,setvar:RESOURCE.bf_counter=+1,expirevar:RESOURCE.bf_counter=300" SecRule REMOTE_ADDR ".*" "phase:5,id:36,t:none,nolog,pass,setvar:IP.bf_counter=+1,expirevar:IP.bf_counter=300" # increase throttling if we're under attack # start throttling after 5 requests SecRule RESOURCE:bf_block "@eq 1" "id:38,phase:5,chain,msg:'too many login attempts occuring, increase throttling',t:none" SecRule IP:bf_counter "@ge 5" "t:none,\ setvar:IP.bf_block,\ setvar:!IP.bf_counter,\ expirevar:IP.bf_block=300" # increase throttling if more than 1000 login attempts total SecRule RESOURCE:bf_counter "@ge 1000" \ "id:40,phase:5,t:none,pass,\ setvar:RESOURCE.bf_block,\ setvar:!RESOURCE.bf_counter,\ expirevar:RESOURCE.bf_block=300" # throttle ip after 100 requests SecRule IP:bf_counter "@ge 100" \ "id:37,phase:5,t:none,pass,\ setvar:IP.bf_block,\ setvar:!IP.bf_counter,\ expirevar:IP.bf_block=300" # jump to this point if user or pass is not passed in request SecMarker END_BRUTE_FORCE_PROTECTION_CHECKS6 </Location>
-->
Member Signup Throttling
Rate-limit to 75 requests to signup/signup.php using the same password or email within a 10 minute period.
<Location /signup/signup.php> # needs to be enabled to read POST data SecRequestBodyAccess On # jump to SecMarker if signup[password] variable is not passed in request SecRule ARGS:signup[password] "^$" "phase:2,id:'981041',t:none,nolog,pass,skipAfter:END_BRUTE_FORCE_PROTECTION_CHECKS1" # set 'RESOURCE' to value of signup[password] if it is not blank SecRule ARGS:signup[password] ".*" "phase:2,id:'1',chain,t:none,nolog,pass" SecAction initcol:RESOURCE=%{ARGS.signup[password]} # enforce an existing block SecRule RESOURCE:bf_block "@eq 1" \ "id:2,phase:2,deny,\ msg:'Password \"%{ARGS.signup[password]}\" blocked because of suspected brute-force attack'" # set or increase count of how many times a signup[password] value was used and have it expire after 600 seconds SecRule ARGS:signup[password] ".*" "phase:5,id:3,t:none,nolog,pass,setvar:RESOURCE.bf_counter=+1,expirevar:RESOURCE.bf_counter=600" # check for too many requests for a single signup[password] value and block future requests that contain that value SecRule RESOURCE:bf_counter "@ge 75" \ "id:4,phase:5,t:none,pass,\ setvar:RESOURCE.bf_block,\ setvar:!RESOURCE.bf_counter,\ expirevar:RESOURCE.bf_block=600" # jump to this point if signup[password] is not passed in request SecMarker END_BRUTE_FORCE_PROTECTION_CHECKS1 # jump to SecMarker if signup[password:1:6:16:::password_check] variable is not passed in request SecRule ARGS:signup[password:1:6:16:::password_check] "^$" "phase:2,id:'981042',t:none,nolog,pass,skipAfter:END_BRUTE_FORCE_PROTECTION_CHECKS2" # set 'RESOURCE' to value of signup[password:1:6:16:::password_check] if it is not blank SecRule ARGS:signup[password:1:6:16:::password_check] ".*" "phase:2,id:'5',chain,t:none,nolog,pass" SecAction initcol:RESOURCE=%{ARGS.signup[password:1:6:16:::password_check]} # enforce an existing block SecRule RESOURCE:bf_block "@eq 1" \ "id:6,phase:2,deny,\ msg:'Password \"%{ARGS.signup[password:1:6:16:::password_check]}\" blocked because of suspected brute-force attack'" # set or increase count of how many times a signup[password:1:6:16:::password_check] value was used and have it expire after 600 seconds SecRule ARGS:signup[password:1:6:16:::password_check] ".*" "phase:5,id:7,t:urlDecode,nolog,pass,setvar:RESOURCE.bf_counter=+1,expirevar:RESOURCE.bf_counter=600" # check for too many requests for a single signup[password:1:6:16:::password_check] value and block future requests that contain that value SecRule RESOURCE:bf_counter "@ge 75" \ "id:8,phase:5,t:none,pass,\ setvar:RESOURCE.bf_block,\ setvar:!RESOURCE.bf_counter,\ expirevar:RESOURCE.bf_block=600" # jump to this point if signup[password:1:6:16:::password_check] is not passed in request SecMarker END_BRUTE_FORCE_PROTECTION_CHECKS2 # jump to SecMarker if signup[email] variable is not passed in request SecRule ARGS:signup[email] "^$" "phase:2,id:'981043',t:none,nolog,pass,skipAfter:END_BRUTE_FORCE_PROTECTION_CHECKS3" # set 'RESOURCE' to value of signup[email] if it is not blank SecRule ARGS:signup[email] ".*" "phase:2,id:'9',chain,t:none,nolog,pass" SecAction initcol:RESOURCE=%{ARGS.signup[email]} # enforce an existing block SecRule RESOURCE:bf_block "@eq 1" \ "id:10,phase:2,deny,\ msg:'Email \"%{ARGS.signup[email]}\" blocked because of suspected brute-force attack'" # set or increase count of how many times a signup[email] value was used and have it expire after 600 seconds SecRule ARGS:signup[email] ".*" "phase:5,id:11,t:none,nolog,pass,setvar:RESOURCE.bf_counter=+1,expirevar:RESOURCE.bf_counter=600" # check for too many requests for a single signup[email] value and block future requests that contain that value SecRule RESOURCE:bf_counter "@ge 75" \ "id:12,phase:5,t:none,pass,\ setvar:RESOURCE.bf_block,\ setvar:!RESOURCE.bf_counter,\ expirevar:RESOURCE.bf_block=600" # jump to this point if signup[email] is not passed in request SecMarker END_BRUTE_FORCE_PROTECTION_CHECKS3 # jump to SecMarker if signup[email:1:1:128:::email_check] variable is not passed in request SecRule ARGS:signup[email:1:1:128:::email_check] "^$" "phase:2,id:'981044',t:none,nolog,pass,skipAfter:END_BRUTE_FORCE_PROTECTION_CHECKS4" # set 'RESOURCE' to value of signup[email:1:1:128:::email_check] if it is not blank SecRule ARGS:signup[email:1:1:128:::email_check] ".*" "phase:2,id:'13',chain,t:none,nolog,pass" SecAction initcol:RESOURCE=%{ARGS.signup[email:1:1:128:::email_check]} # enforce an existing block SecRule RESOURCE:bf_block "@eq 1" \ "id:14,phase:2,deny,\ msg:'Email \"%{ARGS.signup[email:1:1:128:::email_check]}\" blocked because of suspected brute-force attack'" # set or increase count of how many times a signup[email:1:1:128:::email_check] value was used and have it expire after 600 seconds SecRule ARGS:signup[email:1:1:128:::email_check] ".*" "phase:5,id:15,t:none,nolog,pass,setvar:RESOURCE.bf_counter=+1,expirevar:RESOURCE.bf_counter=600" # check for too many requests for a single signup[email:1:1:128:::email_check] and block future requests that contain that value SecRule RESOURCE:bf_counter "@ge 75" \ "id:16,phase:5,t:none,pass,\ setvar:RESOURCE.bf_block,\ setvar:!RESOURCE.bf_counter,\ expirevar:RESOURCE.bf_block=600" # jump to this point if signup[email:1:1:128:::email_check] is not passed in request SecMarker END_BRUTE_FORCE_PROTECTION_CHECKS4 </Location>
Plus script Throttling
Rate-limit the *plus scripts (as well as approved,upgraded,duplicate, and submit scripts) to 75 requests from the same ip address within a 10 minute period
<LocationMatch "/signup/(upgradeplus|approved|upgraded|upsellplus|packageplus|tokenplus|cancelplus|duplicate|verifyplus|signupplus|submit).php"> SecAction initcol:IP=%{REMOTE_ADDR},pass,nolog,id:21 # enforce an existing block SecRule IP:bf_block "@eq 1" \ "id:22,phase:2,deny,\ msg:'IP \"%{REMOTE_ADDR}\" is being throttled'" SecRule REMOTE_ADDR ".*" "phase:5,id:23,t:none,nolog,pass,setvar:IP.bf_counter=+1,expirevar:IP.bf_counter=600" SecRule IP:bf_counter "@ge 75" \ "id:24,phase:5,t:none,pass,\ setvar:IP.bf_block,\ setvar:!IP.bf_counter,\ expirevar:IP.bf_block=600" SecMarker END_BRUTE_FORCE_PROTECTION_CHECKS5 </LocationMatch>
Affiliate Login Throttling
Rate-limit affiliate login attempts to 100, but also dynamically increase rate-limiting to 5 if there are more than 1000 login attempts within a 5 minute period
#Bruteforce checking for affiliate login #Block ip if X amount of hits to internal.php containing user and pass params <Location /internal.php> # needs to be enabled to read POST data SecRequestBodyAccess On # jump to SecMarker if user variable is not passed in request SecRule &ARGS:user "@eq 0" "phase:2,id:'30',t:none,nolog,pass,skipAfter:END_BRUTE_FORCE_PROTECTION_CHECKS6" SecRule ARGS:user "^$" "phase:2,id:'31',t:none,nolog,pass,skipAfter:END_BRUTE_FORCE_PROTECTION_CHECKS6" # jump to SecMarker if pass variable is not passed in request SecRule &ARGS:pass "@eq 0" "phase:2,id:'32',t:none,nolog,pass,skipAfter:END_BRUTE_FORCE_PROTECTION_CHECKS6" SecRule ARGS:pass "^$" "phase:2,id:'33',t:none,nolog,pass,skipAfter:END_BRUTE_FORCE_PROTECTION_CHECKS6" SecAction initcol:IP=%{REMOTE_ADDR},pass,nolog,id:34 # enforce an existing block SecRule IP:bf_block "@eq 1" \ "id:35,phase:2,deny,msg:'IP \"%{REMOTE_ADDR}\" is being throttled, too many affiliate login attempts'" # check if we're getting a ton of login attempts SecAction initcol:RESOURCE=%{REQUEST_FILENAME},pass,nolog,id:39 SecRule REQUEST_FILENAME ".*" "phase:5,id:41,t:none,nolog,pass,setvar:RESOURCE.bf_counter=+1,expirevar:RESOURCE.bf_counter=300" SecRule REMOTE_ADDR ".*" "phase:5,id:36,t:none,nolog,pass,setvar:IP.bf_counter=+1,expirevar:IP.bf_counter=300" # increase throttling if we're under attack # start throttling after 5 requests SecRule RESOURCE:bf_block "@eq 1" "id:38,phase:5,chain,msg:'too many login attempts occuring, increase throttling',t:none" SecRule IP:bf_counter "@ge 5" "t:none,\ setvar:IP.bf_block,\ setvar:!IP.bf_counter,\ expirevar:IP.bf_block=300" # increase throttling if more than 1000 login attempts total SecRule RESOURCE:bf_counter "@ge 1000" \ "id:40,phase:5,t:none,pass,\ setvar:RESOURCE.bf_block,\ setvar:!RESOURCE.bf_counter,\ expirevar:RESOURCE.bf_block=300" # throttle ip after 100 requests SecRule IP:bf_counter "@ge 100" \ "id:37,phase:5,t:none,pass,\ setvar:IP.bf_block,\ setvar:!IP.bf_counter,\ expirevar:IP.bf_block=300" # jump to this point if user or pass is not passed in request SecMarker END_BRUTE_FORCE_PROTECTION_CHECKS6 </Location>
-->