ReCaptcha for contact form.

Although I am not expecting crazy trafic on my website, I decided to add reCaptcha to my Contact Form as a way to avoid spam email. I decided to implement Google Recaptcha instead of trying to verify user using my own method. This is also possible and there are plenty of resources available for Django. One of them would be django-simple-captcha. 

Reason I decided to go for Google's solution is simple- it's the most common and most-secure publicly available captcha engine. I knew this will be useful for me in future as it is industry standard.

Firstly I registered my account at reCaptcha website and added snippet and JS to my contact website. 

 It is important to note that Google will not generate g-recaptcha-response if their div class is outside the form. Please also be vary of other div tags or table tags in your form, google JS might not like them. If you are not getting g-recaptcha-response after submitting the form, this means that you should try to adjust your form. I would recommend testing it in Inspect View of your browser. Just check Network and POST parameters after form is submitted.

After snippet is added, we need to set up server-side verification method. For my custom Contact form I had to add couple of things in views.py to tell Django that it can only send an email after it verifies captcha. Code below can bit a bit complicated to understand but I will try to describe it the best way possible:

def greatsuccess(request):
    messages.success(request, "Email sent!")
    return render(request, 'personal/contact.html')

def greatfail(request):
    messages.error(request, "Invalid Captcha!")
    return render(request, 'personal/contact.html')

def grecaptcha_verify(request):
        data = request.POST
        captcha_rs = data.get('g-recaptcha-response')
        url = "https://www.google.com/recaptcha/api/siteverify"
        headers = {'User-Agent': 'DebuguearApi-Browser',}
        params = {'secret': settings.RECAPTCHA_SECRET_KEY, 'response': captcha_rs}
        verify_rs = requests.post(url,params, headers=headers)
        verify_rs = verify_rs.json()
        response = verify_rs.get("success", False)
        return response 
    
def contact(request):
    if request.method == 'POST':
        response=grecaptcha_verify(request)
        if response == True :
            subject = request.POST.get('subject')
            message = request.POST.get('message')
            email = request.POST.get('email')     
            if subject and message and email:
                try:
                        send_mail('Sent from adamw.eu '\
+subject, message, email, ['myemail@gmail.com'],fail_silently= True)
                except BadHeaderError:
                    return HttpResponse('{Bad Header}')
                return greatsuccess(request)        
            else:
                    return HttpResponse('{Invalid Form}')
        else:
            greatfail(request)
    return render(request, 'personal/contact.html')

Imports:

from django.shortcuts import render 
from django.core.mail import BadHeaderError, send_mail
from django.http import HttpResponse 
from django.contrib import messages
from django.conf import settings
import requests

Let's start from the top.

greatsuccess and greatfail are just methods that use messages framework to generate Success and Fail notifications and render contact page again. I added greatfail method for Invalid Captcha.

grecaptcha_verify method is captcha server-side verification.

  • Firstly we need to get 'g-recaptcha-response' result from post-data of our form, we do that by defining captcha_rs variable.
  • url- this is url that we will be sending all params for verification.
  • headers-  dictionary of HTML headers to be sent along with request(optional).
  • params- this are obligatory parameters that google requires for verification, we are sending our secret key that I specified in my Settings.py file ( this is provided by google). 
  • verify_rs - here we are sending our data to google and later receiving json package with result. we want to get "success" variable, below is example json package from google:
    {
      "success": true|false,
      "challenge_ts": timestamp,  // timestamp of the challenge load 
      "hostname": string,         // the hostname of the site where the reCAPTCHA was solved
      "error-codes": [...]        // optional
    }

     

 To my main contact view i only added another if statement that bassicaly says that if response= True ( which is value we are getting from "success" field from json package) then do what it was doing before, else, render contact.html with fail msg. It's probably best if you compare it to original Contact Form

Comment if something is unclear!

Adios!