When spam bots started to post comments in my blog every day I realized that I need some kind of simple anti-spam protection. I didn't saw yet real anti-spam AI and don't like image captchas. I looked for a mathematical captcha where human is asked to solve a simple mathematical calculation like 3+5=?. Unfortunately I didn't found open-source implementation and write own MathCaptchaForm. A week already I don't see spam posts.
import re
from random import randint
from django import newforms as forms
class MathCaptchaForm(forms.Form):
Q_RE = re.compile("^(\d)\+(\d)$")
A_RE = re.compile("^(\d+)$")
captcha_question = forms.CharField(max_length=10, required=True,
widget=forms.HiddenInput())
captcha_answer = forms.CharField(max_length = 2, required=True,
widget = forms.TextInput(attrs={'size':'2'}))
def __init__(self, *args, **kwargs):
super(MathCaptchaForm, self).__init__(*args, **kwargs)
q = self.data.get('captcha_question') or self._generate_question()
self.initial['captcha_question'] = q
def _generate_question(self):
return "%s+%s" % (randint(1,9), randint(1,9))
def clean_captcha_answer(self):
q = self.Q_RE.match(self.cleaned_data['captcha_question'])
if not q:
raise forms.ValidationError("Are you hacker?")
q = q.groups()
a = self.A_RE.match(self.cleaned_data['captcha_answer'])
if not a:
raise forms.ValidationError("Number is expected!")
a = a.groups()
if int(q[0]) + int(q[1]) != int(a[0]):
raise forms.ValidationError("Are you human?")
Django supports forms subclassing, so to add mathematical captcha to any existing form it is enough to extend MathCaptchaForm. For example comments form used on this site is defined as bellow.
class CommentForm(MathCaptchaForm):
"""Form for editing a comment."""
author = forms.CharField(label='Name', required=False,
max_length=Comment._meta.get_field('author').maxlength,
widget=TextInput(attrs={'size':20}))
url = forms.URLField(label='URL', required=False,
max_length=Comment._meta.get_field('url').maxlength,
widget=TextInput(attrs={'size':24}))
content = forms.CharField(label="Your comment",
max_length=Comment._meta.get_field('content').maxlength,
widget=Textarea(attrs={'cols':80, 'rows': 10})
Then it's only need to add 2 captcha's fields to your template and spam bots along with humans who was bad at school will not be able to post comments. The template may look like:
{{ comment_form.captcha_question }}
<label for="id_captcha_answer">
{% if comment_form.captcha_answer.errors %}
<em class="error">{{ comment_form.initial.captcha_question }}=</em>
{% else %}
{{ comment_form.initial.captcha_question }}=
{% endif %}
</label>
{{ comment_form.captcha_answer }}
Well, it's quite simple to write spam bot that will solve such simple mathematical captchas, but in this case we can improve current simple MathCaptchaForm with integral and differential equations.
I think theres a project named djapthca (django captcha) aswell but if you go the image way you need it to be audioable for blind persons aswell.
I mean.. your solution should be good enough, if spam starts to popin again you can just make some small changes and you are good to go for a while again...
Thanks for sharing the code! maybe you can post it to djangosnippets aswell?
I wanted to have as lightweight captcha as possible, i.e. captcha without PIL and database access (well, I believe that server should not waste resources on such tasks). It quite well protects from bots that fill&submit all found forms, smart bots with memory or eval() on the board will be able to avoid the protection.
Probably, if my small blog will become so popular that spammers will use special scripts for it, I will need to rethink protection mechanism, but now I'm quite happy with it. So it's not a bug, it's a feature :)
Should be relatively easy to implement.
So using hashes without storing state in the database don't protect from replay attacks, that Malcom addressed.
For what you need it probably works, although I've found that integrating Akismet support into the comment approval process goes a long way and doesn't seem to take up much in the way of resources. I'm mostly thinking out loud here, because I like the simplicity of a numerical captcha like this, and I'm considering incorporating into my existing comments system. :-)
It works, it's invisible to real humans, it's quite accurate. I think you could also implement it in less lines of code ;D
http://www.mysoftparade.com/blog/improved-mathematical-captcha/