Integrating reCAPTCHA with Django
This is how I added reCAPTCHA captchas to TrenchMice, a Django-powered website.
Background
We didn’t initially build captchas into TrenchMice, because we simply didn’t think they would be necessary.
By September 2006, the site started receiving spam comments. They were the usual gibberish you see in blog spam: Lots of links, garbage words, and bogus e-mail addresses. (Whenever I see this stuff, I shake my head and wonder why script kidz waste their time generating it. Then I remember it’s because, out of the gazillions of spam messages, some recipients click on the links, making spam financially rewarding. And then I get slightly depressed about the average Internet user. <Sigh.> But, I digress…)
So we broke down and added a captcha system using PIL and our own algorithms. Our images were simple, as modern captchas go:

But they got the job done, with an acceptable load on our servers.
We decided to upgrade the captcha technology for two reasons.
- Lately, we’ve noticed more doorknob-jiggling activity. This hasn’t yet resulted in comment spam, but it indicates the site is getting more attention from spammers. I don’t want to wait until there’s a successful attack to better secure the site.
- We’re not interested in developing a core competency in captcha design. The initial captchas were easy to do, but we don’t want to invest time into learning the latest and greatest imaging techniques now that more work is required.
Why reCAPTCHA?
Their image algorithms are way more sophisticated than ours, and they believe they’re as good as any out there.
Their system does useful work by correcting OCR text from digitized books. This is rather cool.
They claim excellent system availability for their users, and expect to be in business for years. There’s no indications (statistical or anecdotal) to the contrary.
If a hacker cracks their images, they promise to respond quickly by tweaking their algorithms. So we won’t have to do much besides add our voice to the, “Please fix this,” thread that would presumably get created in their support newsgroup.
There was nothing to install. (But PyCrypto was needed for Mailhide.) The API looked fairly easy. And it was free.
The deed
I replaced our template captcha code with this. It’s a straight lift from their client API instructions:
<p>Prove you're not a robot:</p>
<script>var RecaptchaOptions = {theme : 'white'};</script>
<script type="text/javascript" src="http://api.recaptcha.net/challenge?k=PUBLIC_KEY{{ captcha_error }}"></script>
<noscript>
<iframe src="http://api.recaptcha.net/noscript?k=PUBLIC_KEY{{ captcha_error }}"
height="300" width="500" frameborder="0"></iframe><br />
<textarea name="recaptcha_challenge_field" rows="3" cols="40"></textarea>
<input type="hidden" name="recaptcha_response_field" value="manual_challenge"/>
</noscript>
The captcha_error template variable is "&error=ERROR_CODE" if we’re re-displaying a bad form after a POST. Otherwise, it’s an empty string. I vacillated over moving this into the view’s form class for 30 minutes, but I kept it in the template because:
- TrenchMice has a mix of oldforms and newforms, because we agreed to upgrade pages to newforms only if edit them for another reason. (I.e., fixing a bug or changing the form for some other reason.) We haven’t done all of them yet. I didn’t want to procedurally trigger an update of the remain oldforms-based views using captchas; and if I chose to ignore this self-imposed rule, I didn’t want to further burden them with even more code that would have to eventually be updated.
- This is a case where the control–presentation distinction wasn’t clear. Forced to choose one or the other, the reCAPTCHA display belongs in presentation, because it’s JavaScript from another site with core algorithms outside of TrenchMice’s control. Of course, the view has the reCAPTCHA API code to evaluate the user’s response.
I replaced our view captcha code with this. It uses the Python recaptcha-client. (Warning, recaptcha-client didn’t install properly on my system without my tweaking the package files. YMMV.):
# Initialize to an empty string, not None, so the reCAPTCHA call query string
# will be correct if there wasn't a captcha error on POST.
captcha_error = ""
if request.method == 'POST':
## [code snipped]
# Check the form captcha. If not good, pass the template an error code
captcha_response = \
captcha.submit(request.POST.get("recaptcha_challenge_field", None),
request.POST.get("recaptcha_response_field", None),
RECAPTCHA_PRIVATE_KEY,
request.META.get("REMOTE_ADDR", None))
if not captcha_response.is_valid:
captcha_error = "&error=%s" % captcha_response.error_code
elif form.is_valid():
## …
I also swapped out our PIL-based e-mail address obfuscation for the reCAPTCHA Mailhide API. Recaptcha-client had code for this too, and it was easy to hook up. So easy that I won’t bother writing about it.
The end result
The reCAPTCHA captchas work great, and the total amount of view and template code decreased. Our simple captchas had small view hacks to handle the case of re-displaying a form that had a good captcha response but a problem in another field. That code, however minor, is now gone. We also had a background script to clean the captcha image file directory — gone. We also had a font directory for the images — gone.
Visually, the styling isn’t completely in keeping with the rest of the page. But it’s perfectly acceptable. We have been displaying a simple blue box with green text, and I can’t claim that was visually wonderful.
Hi,
I’m one of the engineers on the reCAPTCHA team. Glad to hear you are happy with reCAPTCHA.
About the styling, did you see http://recaptcha.net/apidocs/captcha/client.html — it should let you customize the look and feel to your hearts content. If you didn’t find that, I’d appreciate if you could shoot us an email (support@recaptcha.net) telling where you looked for this information. We want to make the themeing easier to find (I think it’s a slick api, but I’m sort of biased)
- Ben
Comment by Ben Maurer — March 18, 2008 @ 7:31 pm
Hi my name is Harish, and I am a consultant working through http://www.floresense.com
You wrote a nice experience summary.. and enjoyed reading it, since it was in the area of my recent interests. CAPTCHA.
We developed a captcha control for .net2.0 after experiences with other captchas and if you may be interested, you try downloading it from http://www.floresense.com.. I will be happy to have feedback from a person like you using it.
We also have a free, simple captcha service that might be interesting for small sites as well.
You can find these at our website http://www.floresense.com
Thanks for your time
Comment by Harish — March 18, 2008 @ 9:44 pm
@Ben:
Yes, I did read that section. But reCAPTCHA’s styling with the white theme is sufficient for our needs, so we didn’t feel the need to use your custom theme hook.
Our captcha images heretofore haven’t stylistically been anything to write home about. What I didn’t say, but should have clearly said, is that reCAPTCHA is slicker looking than our simple blue rectangle, so it’s actually an improvement.
If we sometime have time to burn (ha!), we’ll look at better styling for it.
Comment by John — March 19, 2008 @ 2:17 pm
@Harish:
Thanks for writing about your company’s captcha control.
From a cursory reading, it looks like a nice solution for a .NET shop. But TrenchMice is written in Django running on Linux!
Your free “RND Plate” service might be applicable. (Why’d you give it that odd name?) But we’re happy with reCAPTCHA’s availability, strength, and the small social service it performs.
Comment by John — March 19, 2008 @ 2:29 pm
[...] Integrating reCAPTCHA with Django « Seek Nuance (tags: django captcha python howto recaptcha webdev programming) [...]
Pingback by The Abarentos Narrative » links for 2008-03-19 — March 19, 2008 @ 3:21 pm