Search Here

Protect site against spam using Google Recaptcha in Django Framework

Protect site against spam using Google Recaptcha in Django Framework

Online spam is the most irritating thing which happens to site users. The problem is that you won’t be able to distinguish between a real user and a bot browsing through your site.
There is a solution for such to protect your site is to use Google Recaptcha V3 API which I’ll teach you in this post of how to protect against bots and spams using Google Recaptcha in Django Rest Framework.

Before getting started you first need to have a google account and also need to create an API Key for Recaptcha in v3 Admin Console.

Create Project And App in Django

We have written a whole post on Installing and Running Django Project. You can go through the get-to-know more about the Django framework.

Client-Side Integration

For Client-Side Integration, there are two ways of doing it. But both the ways are a bit different and here we’ll learn about both the ways.

1. Generating token Automatically

Here the captcha will automatically calculate on the basis of score and add the captcha token within the form when the user clicks the button. On the server side, you can access the token by the name g-recaptcha-response.

<script src="https://www.google.com/recaptcha/api.js"></script>

<form onsubmit="onSubmit( event )" id="contactus-form" method="post">
    <!-- here have your form fields -->

    <button 
    class="g-recaptcha" 
    data-sitekey="<recaptcha_clientside_secret_here>" 
    data-callback='onSubmit' 
    data-action='submit'>Submit</button>
</form>

<script>    
    function onSubmit(token) {
        document.getElementById("contactus-form").submit();
    }
</script>

2. Generating token Programmatically

In this step, you can decide when to invoke the captcha and receive token and this is also good for the custom requirements if you have any on those.

<script src="https://www.google.com/recaptcha/api.js?render=<recaptcha_clientside_secret_here>"></script>

<form onsubmit="onSubmit( event )" id="contactus-form" method="post">
    <!-- here you will have response of recaptcha token -->
    <input type="hidden" name="recaptcha_token" id="recaptcha_token" value="" >

    <!-- here have your form fields -->

    <input type="submit" value="Submit">
</form>

<script>
    function onSubmit(e) {
        e.preventDefault();
        grecaptcha.ready(function() {
            grecaptcha.execute('<recaptcha_clientside_secret_here>', {action: 'submit'}).then(function(token) {
                // here fetch the token and add to form field
                document.getElementById('recaptcha_token').value = token;
                e.target.submit();
            });
        });
    }
</script>

Handling Token on Serverside

On the server side, you have to to a cURL request to https://www.google.com/recaptcha/api/siteverify with the serverside secret key.

Here is a sample working code in Python Django Framework.

import requests

serverside_secret = "<recaptcha_serverside_secret_here>"
url = "https://www.google.com/recaptcha/api/siteverify"
query = {
    "secret" : serverside_secret,
    "response" : request.POST.get('g-recaptcha-response') #if you are using generating token automatically
    #OR
    "response" : request.POST.get('recaptcha_token') #if you are using generating token Programatically
}
response = requests.post( url, data=query )
response_json = response.json()

The method response.json() will give you the JSON response and to verify if the response is correct to use the success key must be True.

Here is the sample success response in JSON.

{
    "success": true,
    "challenge_ts": "2022-01-12T19:15:43Z",
    "hostname": "127.0.0.1",
    "score": 0.9,
    "action": "submit"
}

Here there are two most important keys to look for they are:

  • success: This will return True if the response value is correct.
  • score: This ranges from 0 to 1 and 1 being accurate and 0 being considered as bot.

Note

The captcha response will only work once and if you try to send the response with the same token then you will receive a timeout-or-duplicate error and a similar response as shown below.

{
    "success": false,
    "error-codes": [
        "timeout-or-duplicate"
    ]
}

Integration Recaptcha with Django Framework

I’ll be using an example of the contact us form where I’ll implement Recaptcha.

In main app src\settings.py

RECAPTCHA = {
    'clientside_secret' : 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXX',
    'serverside_secret' : 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXX',
    'verify_url' : 'https://www.google.com/recaptcha/api/siteverify'
}

In contactus\forms.py

from django import forms
import requests
from django.conf import settings

def recaptcha_validator(value):

    query = {
        "secret" : settings.RECAPTCHA['serverside_secret'],
        "response" : value
    }

    response = requests.post( settings.RECAPTCHA['verify_url'], data=query )
    response_json = response.json()

    if response_json['success'] == False:
        error_str = "".join( response_json['error-codes'] )
        raise forms.ValidationError(f"Recaptcha Error : { error_str }")


class ContactForm(forms.Form):
    name  = forms.CharField(max_length=255, required=True, initial="", label="Name*", widget=forms.TextInput( attrs={ 'class' : 'form-control' } ))
    message  = forms.CharField(required=False, initial="", label="Message*", widget=forms.Textarea( attrs={ 'class' : 'form-control', 'rows' : 3 } ))
    phone_no  = forms.CharField(required=False, label="Phone no*" , widget=forms.TextInput( attrs={ 'class' : 'form-control' } ))
    recaptcha_token  = forms.CharField( initial="", validators=[recaptcha_validator] )

In contactus\views.py

from django.shortcuts import render, HttpResponse
from contactus.forms import ContactForm
from django.conf import settings

# Create your views here.
def form(request):
    info = {
        'form' : ContactForm(),
        'recaptcha_setting' : settings.RECAPTCHA
    }

    return render(request, 'contactus/form.html', info)

def save(request):
    info = {
        'form' : ContactForm(request.POST),
        'recaptcha_setting' : settings.RECAPTCHA
    }

    if info["form"].is_valid():
        return HttpResponse("All fields correct.")

    return render(request, 'contactus/form.html', info)
    

In contactus\templates\contactus\form.html

<script src="https://www.google.com/recaptcha/api.js?render={{recaptcha_setting.clientside_secret}}"></script>

<div class="row">
    <div class="col-md-4 offset-md-4">
        
        {% if form.errors %}
            <div class="alert alert-danger" >
                <h4>Form Submission Errors</h4>
                <ul>
                    {% for index, error in form.errors.items %}
                        {{ error }} 
                    {% endfor %}
                </ul>
            </div>
        {% endif %}


        <h2 class="text-center" >Contact Form</h2>
        
        <form onsubmit="onSubmit(event)" action="{% url 'contactus:save' %}" method="post"  >
            {% csrf_token %}

            {{form.recaptcha_token.as_hidden}}
            
            <div>
                <label>{{ form.name.label }}</label>
                {{form.name}}
            </div>
            
            <div>
                <label>{{ form.message.label }}</label>
                {{form.message}}
            </div>

            <div>
                <label>{{ form.phone_no.label }}</label>
                {{form.phone_no}}
            </div>

            <div class="mt-10" >
                <input type="submit" class="btn btn-primary btn-block" value="Save">
            </div>
        </form>


    </div>
</div>

<script>
    function onSubmit(e) {
        e.preventDefault();
        grecaptcha.ready(function() {
            grecaptcha.execute('{{recaptcha_setting.clientside_secret}}', {action: 'submit'}).then(function(token) {
                document.getElementsByName('recaptcha_token')[0].value = token;
                e.target.submit();
            });
        });
    }
</script>

In contactus\urls.py

from django.urls import path
from contactus import views
app_name = "contactus"

urlpatterns = [
    path( 'form', views.form, name="form" ),
    path( 'save', views.save, name="save" ),
]

Custom Validator to Check to validity

In contactus\forms.py, I have created a custom validator to check whether the captcha token is valid to not, and also this code makes validation simpler and easier.

from django import forms
import requests
from django.conf import settings

def recaptcha_validator(value):

    query = {
        "secret" : settings.RECAPTCHA['serverside_secret'],
        "response" : value
    }

    response = requests.post( settings.RECAPTCHA['verify_url'], data=query )
    response_json = response.json()

    if response_json['success'] == False:
        error_str = "".join( response_json['error-codes'] )
        raise forms.ValidationError(f"Recaptcha Error : { error_str }")

Watch Video

Conclusion

This was all about Protect site against spam using Google Recaptcha and Integrating Google Recaptcha in Django Framework. Thank you for reading.