Prevent Cross-Site Request Forgery (CORS) Attacks using Flask WTForms

Cross-Site Request Forgery (CORS) is a vulnerability that allows someone to exploit and take advantage of a user’s logged-in session on a trusted website to make unauthorized requests to a different website that the user trusts.

WTForms is a Flask extension that integrates CSRF protection by default to prevent CORS attacks.

In this article, we will use WTForms to integrate CSRF protection in our Flask web application to make our application resilient against CORS attacks.

Real-World Scenarios of CSRF Vulnerabilities

1. Let’s assume a user is logged into their online banking account and simultaneously visits a malicious site. The malicious site has a hidden form that when submitted, transfers funds from the user’s account to the attacker’s account.

  • Without CSRF protection, the attacker can craft such a form that submits automatically.
  • Since the user is logged in and authenticated into their account, the request is successfully processed without the user’s knowledge.

2. This problem can be addressed by implementing CSRF protection such as provided by WTForms for Flask applications. This mitigates the risk by requiring a unique token for each form submission, without the token the form submission request will be rejected, preventing unauthorized actions even if the user is authenticated.

3. Similar to the above scenario, let’s assume a user is logged into their online shopping account and simultaneously visits a malicious website that has a hidden form that when submitted, adds expensive items and places an order on the online shopping platform.

  • Without CSRF protection, the attacker can craft such a form that submits requests to add items to the user’s cart when the user visits the malicious site.
  • Since the user is authenticated to their online shopping platform, the request is successfully processed without the user’s knowledge.

4. This problem can be addressed by implementing CSRF protection that mitigates the risk by requiring a unique token for each form submission, without the token the form submission request will be rejected, preventing unauthorized actions even if the user is authenticated.

Apply CSRF Protection to Your Flask Application

In this section, we will be securing our application by applying CSRF protection using the flask-wtf package.

Before we begin, make sure to follow the steps in the sections Deploy Vultr Optimized Cloud Instance, Set Up a Python Virtual Environment, and Add Your Demo Application Code Files in our previous article How to Deploy Flask Applications on Vultr using Gunicorn.

1. Install the flask-wtf package.

(myenv) $ sudo pip install flask-wtf

2. Make sure you are in the project directory.

(myenv) $ cd sample

3. Open the app.py file.

(myenv) $ nano app.py

4. Import the CSRFProtect module at the top of the file.

from flask_wtf.csrf import CSRFProtect

5. Add the following configurations in the app.py before the routes.

app.config['SECRET_KEY'] = 'your_secret_key_here'

csrf = CSRFProtect(app)

csrf._csrf_request_token_key = 'X-CSRFToken'

In the above code, we are configuring a secret key and enabling CSRF protection for our Flask web application. Make sure to replace the your_secret_key_here with an actual secret key for your application.

Save and exit the file.

6. Navigate to the templates directory.

(myenv) $ cd templates

7. Open the index.html file.

(myenv) $ nano index.html

8. Edit the HTML page <body> to include the CSRF configuration using WTForms.

<div class="container mt-5">
<h1 class="text-center mb-4">To-Do App</h1>
<form id="task-form">
<input type="hidden" name="csrf_token" value="{{ csrf_token()}}">
<div class="input-group mb-3">
<input type="text" id="task-input" class="form-control" placeholder="Enter a task">
<div class="input-group-append">
<button id="add-task" class="btn btn-primary">Add Task</button>
</div>
</div>
</form>
<ul id="task-list" class="list-group">
</ul>
</div>

<script>
var csrfToken = $('input[name="csrf_token"]').val();

$.ajaxSetup({
    beforeSend: function(xhr, settings) {
    if (!/^(GET|HEAD|OPTIONS|TRACE)$/i.test(settings.type) && !this.crossDomain) {
    xhr.setRequestHeader("X-CSRFToken", csrfToken);
        }
    }
});
</script>

In the above code:

  • We added a form that includes a hidden input field with the csrf_token() function that generates a CSRF token crucial for CSRF protection.
  • Furthermore, the beforesend function is triggered before every AJAX request.
  • An HTTP header named X-CSRFToken is set with the CSRF token value. This ensures that the CSRF token is sent with any requests that may modify data on the server.

Save and exit the file.

9. Exit the templates directory.

(myenv) $ cd ..

10. For the next steps follow the instructions in the section Create a System Service and Configuring Nginx as a reverse proxy in our previous article How to Deploy Flask Applications on Vultr using Gunicorn.

Conclusion

In this article, we secured our Flask web application against CORS attacks by applying CSRF protection using WTForms. It is important to take security measures in production applications to protect essential application resources from malicious attacks.This is a sponsored article by Vultr. Vultr is the world’s largest privately-held cloud computing platform. A favorite with developers, Vultr has served over 1.5 million customers across 185 countries with flexible, scalable, global Cloud Compute, Cloud GPU, Bare Metal, and Cloud Storage solutions. Learn more about Vultr.