CSRF protection with a React Form, a Flask server, and Flask-WTF
Asked Answered
S

2

7

TL;DR I need to protect my form from CSRF attacks and I want to use ReactJS for the frontend and Flask/Flask-WTF for the backend.

I’m refactoring a website built with Python, Flask, and Flask-WTF for forms and I want to use React for the frontend rather than Jinja2 through PyPugjs. I’m using Flask-WTF to render the forms and it takes care of the CSRF tokens and such. I know how to make a form with React but how do I get CSRF protection?

Right now my form rendering looks like this: (uses Pug)

mixin render_form(form, id='', action='Submit')
    form(method='POST', action='', id=id)
        =form.csrf_token

        each field in form
            fieldset
                if field.errors
                    each error in field.errors
                        .notification.error
                            #{error}

                #{field(placeholder=field.label.text)}

        button(type='submit') #{action}
Secundas answered 24/3, 2017 at 17:25 Comment(1)
The question should rather be 'How do I add an input field with React + Python'. CSRF Protection is just a field with a random value provided and then validated by the server.Chrome
N
4

You can throw {{ csrf_token() }} in a meta tag in index.html

 <meta  id="csrf-token" content={{csrf_token()}}>

then when you wanna post/fetch, just add it to ur headers with

export const post = (path, data={}) => {

const options = {
    method: 'POST', 
    headers: {
        // 'Accept': 'application/json; charset=utf-8',
        // 'Content-Type': 'application/json; charset=utf-8',
        // 'Cache': 'no-cache',
        // 'X-Requested-With': 'XMLHttpRequest', 
        'X-CSRFToken': document.getElementById("csrf-token").getAttribute("content")
    }, 
    body: data
};

return fetch(path, options);
}

p.s. this still feels hacky and I'm still looking for a more reacty way

Nur answered 12/5, 2020 at 21:46 Comment(1)
Yeah, I probably shouldn't have accepted that other answer. I haven't used flask or dealt with csrf protection since I wrote this question (I ended up scrapping that particular project) but I think your solution would work!Secundas
A
6

You'll need to send the csrf token as the header X-CSRFToken when you POST the form. See their docs here: http://flask-wtf.readthedocs.io/en/stable/csrf.html#javascript-requests

Their example w/ POSTing via jQuery sets the X-CSRFToken before sending any POST/PUT/DELETE ajax requests:

<script type="text/javascript">
    var csrf_token = "{{ csrf_token() }}";

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

Depending on what library you're using to send the form POSTs back to the server, your implementation of setting the header X-CSRFToken will be different.

Anishaaniso answered 29/3, 2017 at 12:8 Comment(7)
Well this is interesting but I need to know how to implement it with React. Could you help me with that?Secundas
Also, I don’t want to use Jinja to embed the CSRF token. Isn’t there a better way? @AnishaanisoSecundas
@Secundas render a CSRF token and pass it into your React app, then continue to pass it along with whatever ajax lib you're React app is using? What ajax library are you using?Anishaaniso
How should I pass it though? I don’t want to put it into the HTML; just send it to the JS. I’d probably use fetch or something. Not using one atm.Secundas
to "send it to the JS", you're going to have to "put it in the HTML", unless your JS is generated via Jinja.Anishaaniso
isn't there another way to do it?Secundas
I wish this answer wasn't accepted because it sux and doesn't answer the react implementation question. this is just straight out of the wtf forms docNur
N
4

You can throw {{ csrf_token() }} in a meta tag in index.html

 <meta  id="csrf-token" content={{csrf_token()}}>

then when you wanna post/fetch, just add it to ur headers with

export const post = (path, data={}) => {

const options = {
    method: 'POST', 
    headers: {
        // 'Accept': 'application/json; charset=utf-8',
        // 'Content-Type': 'application/json; charset=utf-8',
        // 'Cache': 'no-cache',
        // 'X-Requested-With': 'XMLHttpRequest', 
        'X-CSRFToken': document.getElementById("csrf-token").getAttribute("content")
    }, 
    body: data
};

return fetch(path, options);
}

p.s. this still feels hacky and I'm still looking for a more reacty way

Nur answered 12/5, 2020 at 21:46 Comment(1)
Yeah, I probably shouldn't have accepted that other answer. I haven't used flask or dealt with csrf protection since I wrote this question (I ended up scrapping that particular project) but I think your solution would work!Secundas

© 2022 - 2024 — McMap. All rights reserved.