Integrating Google Recaptcha to Django

Like many, I hate captchas. They are tedius and put the burden on your real users to prove they are real. I prefer whenever possible to try to implement a honeypot anti-spam layer. Recently, however I had a site with public forms that a honeypot wasn’t doing an adequate job of protecting.

How does it work?

At a very basic level, there is a block of html containing your public key that you add to your html form. A google hosted JS script hijacks and makes the html block into a recaptcha element. When a person checks the checkbox, the JS reaches out to the recaptcha service and retrieves a one-time token that gets submitted with the form via a hidden field.

In your backend code, you take the token and a secret key provided to you at signup and then send them to the service API URL via a post. The service API returns a JSON string representing success or failure. Based on the success or failure of this API call, validate the form appropriately.

How to integrate into Django

Settings

Add some settings to your Django project. You want these credentials to be managed at an environment level and not embedded in your code.

Then, in your form test the recaptcha token and user’s IP against the recaptcha serice by utilizing the request object that you passed to the form in the step above.

fromdjangoimportformsfromdjango.confimportsettingsfromdjango.utils.translationimportugettextas_importurllibimporturllib2importjsonclassMyForm(forms.ModelForm):classMeta:model=MyModeldef__init__(self,*args,**kwargs):# make the request object available to the form objectself.request=kwargs.pop('request',None)super(MyForm,self).__init__(*args,**kwargs)defclean(self):super(MyForm,self).clean()# test the google recaptchaurl="https://www.google.com/recaptcha/api/siteverify"values={'secret':settings.GOOGLE_RECAPTCHA_SECRET_KEY,'response':self.request.POST.get(u'g-recaptcha-response',None),'remoteip':self.request.META.get("REMOTE_ADDR",None),}data=urllib.urlencode(values)req=urllib2.Request(url,data)response=urllib2.urlopen(req)result=json.loads(response.read())# result["success"] will be True on a successifnotresult["success"]:raiseforms.ValidationError(_(u'Only humans are allowed to submit this form.'))returnself.cleaned_data