Gunnar Wolf: Fighting spam on roundcube with modsecurity
Every couple of months, one of my users falls prey to phishing
attacks, and send their login/password data to an unknown somebody
who poses as Well, as me, their always-friendly and always-helpful
systems administrator.
What follows is, of course, me spending a week trying to get our
systems out of all of the RBLs/DNSBLs. But, no matter how fast I act,
there s always distruption and lost mails (bounced or classified as
spam) for my users.
Most of my users use the Webmail I have configured on our institute s
servers, Roundcube, for which I have the
highest appreciation. Only that Of course, when a user yields their
username and password to an attacker, it is very successful
at Sending huge amounts of unrequested mail, leading to my server
losing its reputation
This week, I set two bits of mitigation strategies. The first one,
most straightforward, was to ask Roundcube to disallow sending mails
with over ten recipients. In a Debian install, this is as easy as
setting up the following variable in
However, a dilligent spammer can still clog the server by sending
many, many, many, many requests maybe each of them with ten
recipients only; last weekend, I got a new mail every three seconds or
so.
Adding rate limit to a specific Roundcube action is not easy,
however, or at least it took me quite a bit of headbanging to get it
right . Roundcube is a very AJAX-y system where all (most, at least)
actions are received by
The first line specifies the rule will match request lines specifying
the POST verb aind including the
I truly, truly hope this is the last time my server falls in the black
pits of DNSBL/RBL lists
/etc/roundcube/config.inc.php
:
$config['max_recipients'] = 10
/index.php
and there is quite a bit of
parsing to do to understand the actions done. When sending a mail, of
course, it is done using the POST
HTTP verb, and the URI-specified
variables include _task=mail&_unlock=loading<message_id>
(of course,
with changing message IDs).
After some poking here and there, I faced to SpiderLabs
ModSecurity Only
that I am not yet well versed in writing rules for it. But after quite
a bit of reading, poking, breaking I was able to come up with the
following rules:
# How often does the limit counter expire ratelimit_client=60,
# every 60 seconds
SecRule REQUEST_LINE "@rx POST.*_task=mail&_unlock" id:10,phase:2,nolog,pass,setuid:% tx.ua_hash ,setvar:user.ratelimit_client=+1,expirevar:user.ratelimit_client=60
# How many requests do we allow in the specified time period?
# @gt 3, 3 requests
SecRule user:ratelimit_client "@gt 2" chain,id:100009,phase:2,deny,status:429,setenv:RATELIMITED,log,msg:RATE-LIMITED
SecRule REQUEST_LINE "@rx POST.*_task=mail&_unlock"
_task=mail&_unlock
fragment in the
URL. It increments tht ratelimit_client
user variable, but expires
it after 60 seconds.
The first line verifies whether the above specified variable (do note
that it s user:
instead of user.
) is greater than 2. If so, it
sets the deny
action, HTTP return status of 429 (Too Many
Requests
), and logs the reason why this request was denied
(rate-limited).
And Given the way Roundcube works, this even works transparently!
If a user hits the limit, the mail sending component will just wait
and, after a while, time out. Then, the user can click Send
again. If legitimate users are too productive and try to send over
three mails in a minute, they won t lose any of it; spammers will
(hopefully!) find it unbearably slow and give up.
Logging is quite informative; I will probably later restrict it to
show fewer parts (even if just for privacy sake, as it logs the full
request!) For a complex permissions framework such as mod_security,
having information such as the following is most welcome in order to
find a possibly misbehaving rule:
--76659f4b-H--
Message: Access denied with code 429 (phase 2). Pattern match "POST.*_task=mail&_unlock" at REQUEST_LINE. [file "/etc/modsecurity/rate_limit_sender.conf"] [line "20"] [id "100009"] [msg "RATELIMITED BOT"]
Apache-Error: [file "apache2_util.c"] [line 273] [level 3] [client 192.168.1.48] ModSecurity: Access denied with code 429 (phase 2). Pattern match "POST.*_task=mail&_unlock" at REQUEST_LINE. [file "/etc/modsecurity/rate_limit_sender.conf"] [line "20"] [id "100009"] [msg "RATELIMITED BOT"] [hostname "my.server.mx"] [uri "/roundcube/"] [unique_id "YMzJLR9jVDMGsG@18kB1qAAAAAY"]
Action: Intercepted (phase 2)
Stopwatch: 1624033581838813 1204 (- - -)
Stopwatch2: 1624033581838813 1204; combined=352, p1=29, p2=140, p3=0, p4=0, p5=94, sr=81, sw=89, l=0, gc=0
Response-Body-Transformed: Dechunked
Producer: ModSecurity for Apache/2.9.3 (http://www.modsecurity.org/).
Server: Apache
WebApp-Info: "default" "-" ""
Engine-Mode: "ENABLED"