Secure Form With CSRF Token in Flask

Most of the applications would implement Forms as per there requirements. That could be like Contact Form, Register / Login Form, etc. It is easy for a spammer to inject unwanted content into Unsecured Forms. In this tutorial, we will learn how to add validation rules and CSRF token. We are using Flask to create the basic registration form.

Table of content

Protect Form with CSRF Token

CSRF Token is basically a unique, secret, unpredictable value that is generated by server-side applications.

We will use the WT Forms module to protected our Form. It is easy to integrate with your application. WT Forms will help to validate the form request fields. We will implement both the CSRF and the form validation with our registration form.

Integrate WTForms with Flask

We are going to integrate the WTForm with the registration form. We have several fields required to complete the registration form i.e. First and Last Name, Email and Password.

Flask Registration Form
<form action="{{ url_for('register') }}" method="POST">
    <div class="row">
        <div class="col-6 form-group">
            <label>First Name</label>
            <input type="text" name="first_name" placeholder="First Name" class="form-control">
        </div>
        <div class="col-6 form-group">
            <label>Last Name</label>
            <input type="text" name="last_name" placeholder="Last Name" class="form-control">
        </div>
        <div class="col-12 form-group">
            <label>Email</label>
            <input type="email" name="email" placeholder="Your Email" class="form-control">
        </div>
        <div class="col-12 form-group">
            <label>Password</label>
            <input type="password" name="password" class="form-control">
        </div>
        <div class="col-12 form-group">
            <button class="btn btn-primary">Register</button>
        </div>
    </div>
</form>

Setup CSRF Protection

First, we need to set up the CSRF Protection that will automatically check for the csrf token for each post request. You need a secret key to generate the CSRF Token.

from flask import Flask
from flask_wtf.csrf import CSRFProtect
import os

csrf = CSRFProtect()

app = Flask('__name__', template_folder="./myapp/templates/")
app.config['SECRET_KEY'] = 'ty4425hk54a21eee5719b9s9df7sdfklx'
csrf.init_app(app)

Let’s start to integrate the WTForm and write some basic validation rules. First, create a file forms.py and import the following modules that we required to write validation rules.

from flask_wtf import Form
from wtforms import StringField, PasswordField
from wtforms.validators import DataRequired, Email

The next step is to add the class with RegistrationFrom().

...
...

class RegistrationForm(Form):
    first_name = StringField('First Name', validators=[DataRequired()])
    last_name = StringField('Last Name', validators=[DataRequired()])
    email = StringField('Email', validators=[DataRequired(), Email()])
    password = PasswordField('Password', validators=[DataRequired()])

That’s it for the forms.py class. Now open the file that has the route for the registration process and import our RegistraionForm class. I have my routes in the routes.py file and this is how it looks.

from flask import render_template, redirect, url_for, jsonify

from . import app
from .forms import RegistrationForm

@app.route('/')
def index():
    form = RegistrationForm()
    return render_template('auth/registration.html', form=form)

Now reopen the registration.html file and modify it.

<form action="{{ url_for('register') }}" method="POST">
    {{ form.csrf_token }}
    <div class="row">
        <div class="col-6 form-group">
            <label>First Name</label>
            <input type="text" name="first_name" placeholder="First Name" class="form-control">
            {% for error in form.first_name.errors %}
                <small class="text-danger">{{ error }}</small>  
            {% endfor %}
        </div>
        <div class="col-6 form-group">
            <label>Last Name</label>
            <input type="text" name="last_name" placeholder="Last Name" class="form-control">
            {% for error in form.last_name.errors %}
                <small class="text-danger">{{ error }}</small>  
            {% endfor %}
        </div>
        <div class="col-12 form-group">
            <label>Email</label>
            <input type="email" name="email" placeholder="Your Email" class="form-control">
            {% for error in form.email.errors %}
                <small class="text-danger">{{ error }}</small>  
            {% endfor %}
        </div>
        <div class="col-12 form-group">
            <label>Password</label>
            <input type="password" name="password" class="form-control">
            {% for error in form.password.errors %}
                <small class="text-danger">{{ error }}</small>  
            {% endfor %}
        </div>
        <div class="col-12 form-group">
            <button class="btn btn-primary">Register</button>
        </div>
    </div>
</form>

As you see that we have used the {{ form.csrf_token }}. This piece of code will generate the following field with the CSRF Token.

<input id="csrf_token" name="csrf_token" type="hidden" value="CSRF_TOKEN_STRING">

And the following codes help to display the validation error for each field.

{% for error in form.first_name.errors %}
    <small class="text-danger">{{ error }}</small>  
{% endfor %}

The next step is to open the routes.py file and create the /register route that accept the POST request.

@app.route('/')
def index():
    form = RegistrationForm()
    return render_template('auth/registration.html', form=form)

@app.route('/register', methods = ['POST'])
def register():
    form = RegistrationForm()
    if form.validate_on_submit():
        print('Submission successful')
        return redirect(url_for('index'))
    
    return render_template('auth/registration.html', form=form)

The registration route will check the validation with the help of form.validate_on_submit() and return true on successful validation. And the CSRF token is automatically checked with the POST request.

I hope you enjoy the reading of this post. If you have any suggestions or questions, please feel free to use comment section.

Posted by Jogesh Sharma

Jogesh Sharma is a web developer and blogger who loves all the things design and the technology, He love all the things having to do with PHP, WordPress, Joomla, Magento, Durpal, Codeigniter, jQuery, HTML5 etc. He is the author of this blog.