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}
Hum4n01d
  • 1,312
  • 3
  • 15
  • 30
  • 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. – Marco Mar 27 '17 at 01:18

2 Answers2

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.

kavun
  • 3,358
  • 3
  • 25
  • 45
  • 1
    Well this is interesting but I need to know how to implement it with React. Could you help me with that? – Hum4n01d Mar 31 '17 at 18:11
  • 1
    Also, I don’t want to use Jinja to embed the CSRF token. Isn’t there a better way? @kavun – Hum4n01d Mar 31 '17 at 18:14
  • @Hum4n01d 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? – kavun Apr 04 '17 at 21:46
  • 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. – Hum4n01d Apr 04 '17 at 22:40
  • to "send it to the JS", you're going to have to "put it in the HTML", unless your JS is generated via Jinja. – kavun Apr 05 '17 at 13:41
  • isn't there another way to do it? – Hum4n01d Apr 05 '17 at 21:07
  • 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 doc – blah blah May 10 '20 at 17:19
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

Hum4n01d
  • 1,312
  • 3
  • 15
  • 30
blah blah
  • 425
  • 6
  • 12
  • 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! – Hum4n01d May 13 '20 at 03:09