How to add csrf to flask app without wtforms?
Asked Answered
F

2

8

I have a simple web app, And I want to add csrf protection. But I didn’t understand the csrf wrapper provided by Flask-WTF. I've already seen the docs. But still didn’t understand how it works.

My questions is:

(1) After wrapping the app, Do I need to handle it from the route? Or flask take care of that for me?

(2) If Not how to handle it myself? (Please provide an example).

Note: I Don't want to use wtf forms, I wanted to use custom tags for inputs.

app.py :

from flask import Flask, render_template
from flask_wtf.csrf import CSRFProtect

app = Flask(__name__)
app.config['SECRET_KEY'] = 'secret'
csrf = CSRFProtect(app)

@app.route('/', methods=['GET'])
def get_home():
    """Get home template"""
    return render_template('home.html')

@app.route('/', methods=['POST'])
def post_home():
    """Handle posted data and do stuff"""
    return

home.html (form):

<form action="#" method="post">
    <input type="hidden" name="csrf_token" value="{{ csrf_token() }}"/>
    <input type="text" placeholder="Name">
    <button type="submit">
        Submit
    </button>
</form>
Fretwell answered 11/9, 2020 at 16:57 Comment(0)
U
1

By default, you don't need to worry about validating it yourself - you simply deal with the other fields of the POST request as normal. If you look at the function csrf_protect() within the init_app function of the CSRFProtect class here (lines 202-225, https://github.com/lepture/flask-wtf/blob/master/flask_wtf/csrf.py), you can view the things which will stop the protect() function being run before a given request.

Unwilling answered 25/11, 2020 at 4:36 Comment(1)
Link has changed (file was moved to a "src" subdirectory" -- this should be a permalink: github.com/wtforms/flask-wtf/blob/…Liliuokalani
L
1

The key to how this works is the csrf_token() jinja template function, which you are using in your home.html form as the csrf_token hidden input field value, and flask's @before_request decorator function which will call CSRFProtect.protect() before every request.

In detail:

You can see in the linked code, that csrf_token() jinja template points to the generate_csrf() function, and this is where the token automatically gets generated for you based on the current session and request, utilizing URLSafeTimedSerializer to generate the protected token.

Then, we can see in the init_app() function (which will be called if you pass your flask app directly into the CSRFProtect constructor as your app.py code does), that it uses the flask app's @app.before_request decorator to call csrf_protect() before every flask request. If the request is eligible for protection, it will call CSRFProtect.protect(), which will verify the token generated by the jinja template (stored in the flask current_app config for the lifetime of the request) and will be sent by the browser on form submit.

This is the basic flow you would need to implement if you wanted to do it yourself. Generate a CSRF token on a new request, supply it to the client, and have the client send it back on form submit. You will need to take care that in implementation, the token does not become stale, as the strength of CSRF protection relies on a new token being generated with enough frequency that attackers could never brute force it.

Your question stated that you didn't want to use WTF Forms because you "wanted to use custom tags for inputs". Using custom tags for form inputs is very easy with CSRFProtect. All you need to do is update the flask app's config to define the property WTF_CSRF_FIELD_NAME to be the name of your custom tag. Then CSRFProtect will look for the request form input corresponding to that name instead of the default name csrf_token

Liliuokalani answered 15/8, 2023 at 17:28 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.