CSRF Protection in Flask

Last Updated : 8 Jun, 2026

Cross-Site Request Forgery (CSRF) is a security vulnerability where an attacker tricks a user into unknowingly submitting a request to a web application in which they are authenticated. This can lead to unauthorized actions being performed on behalf of the user, such as changing account settings or making transactions.

Preventing CSRF Attacks

CSRF attacks can be prevented by using CSRF tokens, which are unique values generated by the server and included in forms. The server verifies these tokens on submission, ensuring that only legitimate requests are processed.

2056958237
Preventing CSRF Attacks

Implementing CSRF Protection in Flask

Step 1: Install Dependencies

To implement CSRF protection in Flask, install the required packages using:

pip install flask flask-wtf

Step 2: Set Up Flask Application

In Flask, CSRF protection can be enabled using Flask-WTF, which provides automatic CSRF protection for forms. Below is a basic example:

Python
from flask import Flask, render_template, request, flash
from flask_wtf import FlaskForm, CSRFProtect
from wtforms import StringField, SubmitField
from wtforms.validators import DataRequired

app = Flask(__name__)
app.secret_key = 'your_secret_key'  # Required for CSRF protection
csrf = CSRFProtect(app)  # Enable CSRF Protection

# Creating a FlaskForm to manage CSRF properly
class NameForm(FlaskForm):
    name = StringField('Name', validators=[DataRequired()])
    submit = SubmitField('Submit')

@app.route("/", methods=['GET', 'POST'])
def index():
    form = NameForm()

    if request.method == 'POST':
        if form.validate_on_submit():
            name = form.name.data
            flash(f'Hello {name} from Protected Form!', 'success')
            return render_template('index.html', form=form)
        else:
            flash('Invalid form submission!', 'danger')

    return render_template('index.html', form=form)

@app.route("/unprotected_form", methods=['POST'])
def unprotected_form():
    name = request.form.get('Name', '').strip()
    if not name:
        return "Error: Name is required!", 400
    return f'Hello {name} from Unprotected Form!'

if __name__ == '__main__':
    app.run(debug=True)

Explanation:

  • CSRFProtect(app) to automatically validate CSRF tokens for incoming form submissions and protect the application from forged requests.
  • Defines the NameForm class using FlaskForm, which includes a name input field and a submit button along with built-in CSRF protection.
  • Applies the DataRequired() validator to ensure that the name field is not submitted empty.
  • The index() route displays the form and processes form submissions using the validate_on_submit() method.
  • Checks whether the submitted form contains a valid CSRF token before processing the request and displaying a success message.
  • Uses Flask's flash() function to show success or error messages based on the form submission result.

Step 3: Create a Secure HTML Form

A simple HTML page with CSRF-protected and unprotected forms:

Python
<!DOCTYPE html>
<html>
<head>
    <title>CSRF Protection Demo</title>
</head>
<body>

    <h2>Protected Form (CSRF Token Required)</h2>
    <form action="{{ url_for('index') }}" method="POST">
        {{ form.hidden_tag() }}  <!-- This automatically includes CSRF token -->
        <label for="Name">Your Name Please?</label>
        {{ form.name() }}  <!-- Flask-WTF handles input -->
        {{ form.submit() }}  <!-- Submit button -->
    </form>

    <h2>Unprotected Form (No CSRF Token)</h2>
    <form action="{{ url_for('unprotected_form') }}" method="POST">
        <label for="Name">Your Name Please?</label>
        <input type="text" name="Name" required>
        <button type="submit">Submit</button>
    </form>

    <!-- Flash Messages -->
    {% with messages = get_flashed_messages(with_categories=True) %}
        {% if messages %}
            {% for category, message in messages %}
                <p style="color: {{ 'red' if category == 'danger' else 'green' }}">{{ message }}</p>
            {% endfor %}
        {% endif %}
    {% endwith %}

</body>
</html>

Explanation: hidden_tag() automatically renders hidden form fields required by Flask-WTF, including the CSRF token that is validated when the form is submitted.

Step 4: Run the Flask Application

Run the app in development mode using command-

python app.py

Step 5: Testing the CSRF Protection

Open http://127.0.0.1:5000/ in your browser.

Try submitting both forms:

Unprotected Form: Submits without any token, making it vulnerable to CSRF attacks.

Protected Form: Requires a valid CSRF token to submit successfully.

If you try submitting the protected form without the CSRF token, an error will occur, preventing unauthorized requests.

c1
Value into unprotected form
c2
Successful submission
c3
Value into Protected form
c4
CSRF token missing error on submission

Note: To submit forms that require CSRF tokens, use hidden_tag() method of Flask-WTF, it automatically generates hidden fields including CSRF token inside a form.

Comment