Fighting comment spam seams like a never ending battle. I've done a lot over the last few years to try and squash it on my blog.
I started out by implementing a list of words that would trigger the comment to be blocked. I found myself updating this list on a weekly basis, never staying on top of it.
I solved that problem by implementing the Bayesian Filter CFC from fusionKit. The bayes filter has worked really well, and produced a surprisingly low number of false positives.
Now just over the weekend I was flooded with a ton of comment spams that were written well enough to get past the bayes filter. They were also all submitted within a few minutes, so there was no time to train my bayes filter.
I'm not a big fan of statically blocking IP addresses, since the owners of IP addresses can change over time. However I think temporary blocks on IP's are OK, so I wrote a little rate limiter that will block IP's that try to post more than 1 comment within a 5 minute time span or IP's that have attempted to post a large number of comments.
I'm sure some of you have probably experienced the same problem, so here you go:
<cfif IsDefined("application.rate_limiter")>
<cfif StructKeyExists(application.rate_limiter, CGI.REMOTE_ADDR)>
<cfif application.rate_limiter[CGI.REMOTE_ADDR].attempts GT 1 AND DateDiff("n", application.rate_limiter[CGI.REMOTE_ADDR].last_attempt, Now()) LT 5>
<p>You are posting too many comments too fast, please slow down and wait 5 min.</p>
<cfset application.rate_limiter[CGI.REMOTE_ADDR].attempts = application.rate_limiter[CGI.REMOTE_ADDR].attempts + 1>
<cfset application.rate_limiter[CGI.REMOTE_ADDR].last_attempt = Now()>
<cfabort>
<cfelseif application.rate_limiter[CGI.REMOTE_ADDR].attempts GT 20>
<p>You have made too many attempts to post a comment. Please try back in a few days.</p>
<cfset application.rate_limiter[CGI.REMOTE_ADDR].attempts = application.rate_limiter[CGI.REMOTE_ADDR].attempts + 1>
<cfset application.rate_limiter[CGI.REMOTE_ADDR].last_attempt = Now()>
<cfabort>
<cfelse>
<cfset application.rate_limiter[CGI.REMOTE_ADDR].attempts = application.rate_limiter[CGI.REMOTE_ADDR].attempts + 1>
<cfset application.rate_limiter[CGI.REMOTE_ADDR].last_attempt = Now()>
</cfif>
<cfelse>
<cfset application.rate_limiter[CGI.REMOTE_ADDR] = StructNew()>
<cfset application.rate_limiter[CGI.REMOTE_ADDR].attempts = 1>
<cfset application.rate_limiter[CGI.REMOTE_ADDR].last_attempt = Now()>
</cfif>
<cfelse>
<cfset application.rate_limiter = StructNew()>
<cfset application.rate_limiter[CGI.REMOTE_ADDR] = StructNew()>
<cfset application.rate_limiter[CGI.REMOTE_ADDR].attempts = 1>
<cfset application.rate_limiter[CGI.REMOTE_ADDR].last_attempt = Now()>
</cfif>
Comments
Pete, check out this project from Jake Munson cfformprotect.riaforge.org
Have your tried the CFAkismet CFC? http://devnulled.com/cfakismet We put it on our blog (http://blog.d-p.com) and love it.
@Pete: You might want to adjust the 1 comment per 5 minutes to at least 2 commments. 1 seems a little to strict--especially in the case where someone wants to post an addendum to what they typed.
Hey Dan, Actually it does allow for two comments since it says GT 1 and the attempts are incremented after that point. I wasn't clear about that in my post however. Thanks for pointing that out.
I can understand your code. Could you give me the PHP version please
I have a blog (Fugitive Seeking Truth) and have had a number of my posts comment-spammed with the same comment that appears several times on this page: "Thanks for sharing the link - but unfortunately it seems to be down? Does anybody here at [my blog address] have a mirror or another source?" with a name attached. I also have the comment requesting permission to backlink. Any idea who posts these comments and why? Thanks, Blessings and peace, Ylanne