Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

CSRF protection on AJAX authentication in Flask

I'd like to AJAXify both a login and a signup form on a site. Up to now I've been using WTForms mainly for its built-in CSRF protetion, but for this project I didn't feel like it was worth it -- an extra layer of abstraction, and therefore frustration, for something that should be pretty simple.

So I came across this snippet on Flask's security section:

@app.before_request
def csrf_protect():
    if request.method == "POST":
        token = session.pop('_csrf_token', None)
        if not token or token != request.form.get('_csrf_token'):
        abort(403)

def generate_csrf_token():
    if '_csrf_token' not in session:
        session['_csrf_token'] = some_random_string()
    return session['_csrf_token']

app.jinja_env.globals['csrf_token'] = generate_csrf_token

I understand the thought process behind this code. In fact, it all makes perfect sense to me (I think). I can't see anything wrong with it.

But it doesn't work. The only thing I've changed about the code is replacing the pseudofunction some_random_string() with a call to os.urandom(24). Every request has 403'd so far because token and request.form.get('_csrf_token') are never the same. When I print them this becomes obvious -- usually they're different strings, but occasionally, and seemingly with no underlying reason, one or the other will be None or a truncated version of the output of os.urandom(24). Obviously something out of sync, but I'm not understanding what it is.

like image 378
Chase Ries Avatar asked Oct 23 '14 19:10

Chase Ries


People also ask

Does Flask have CSRF protection?

To enable CSRF protection globally for a Flask app, register the CSRFProtect extension. CSRF protection requires a secret key to securely sign the token. By default this will use the Flask app's SECRET_KEY . If you'd like to use a separate token you can set WTF_CSRF_SECRET_KEY .

How do I generate CSRF token Flask?

CSRF Token WorkflowThe client sends a POST request with their credentials to authenticate. If the credentials are correct, the server generates a session and CSRF token. The request is sent back to the client, and the session is stored in a cookie while the token is rendered in a hidden form field.

How do I enable CSRF Cookies?

Open Chrome Settings. In the Privacy and security section, click Cookies and other site data. Scroll down to Sites that can always use cookies and click Add.

What is FlaskForm?

FlaskForm Class. Flask provides an alternative to web forms by creating a form class in the application, implementing the fields in the template and handling the data back in the application. A Flask form class inherits from the class FlaskForm and includes attributes for every field: class MyForm(FlaskForm):


1 Answers

You can get the convenience of flask-wtf without all the heaviness, and without rolling your own:

from flask_wtf.csrf import CsrfProtect

then on init, either:

CsrfProtect(app)

or:

csrf = CsrfProtect()

def create_app():
    app = Flask(__name__)
    csrf.init_app(app)

The token will then be available app-wide at any point, including via jinja2:

<form method="post" action="/">
  <input type="hidden" name="csrf_token" value="{{ csrf_token() }}" />
</form>

(via the docs)

like image 118
scharfmn Avatar answered Sep 29 '22 09:09

scharfmn