Forums

Missing CSRF tokens on multiple sessions

Hi,

I've created a basic flask app with a login + register page that has CSRF protection. When I password-protect my pythonanywhere site and then create an account and login on my app, it works perfectly fine.

However, if I log in on a different browser (i.e., log in on Chrome and then incognito), I run into a "Bad Request: Missing CSRF Token" error. I've triple-checked the code, and I'm pretty sure it's fine, and I'm more suspicious of pythonanywhere being the cause.

I read elsewhere in the forums that the free tier of pythonanywhere only provides 1 web worker; would this be the cause? If we are expecting it to be possible to do the above action on the free tier of pythonanywhere, I'll try and figure out where I've gone wrong.

Cheers.

The most likely cause of that is that your browser is blocking the CSRF in some way. Check that the CSRF token is in the form and also check the developer tools network tab in your browser to see whether the CSRF headers/cookie are being blocked.

I'm not sure if the cookie is being blocked, but the csrf_token in the payload in the second browser is the same csrf_token in the first browser (despite the second browser generating its own csrf_token in its form), so that's probably where the issue lies. Cheers for the tip.

Any pointers on what could be the cause/where to investigate from here?

How are you injecting the csrf_token into the form in the page?

It gets inserted via the form.hidden_tag(). See the chunk of html on the login page below (and note the registration page is basically the same chunk of code):

<body>
{% if current_user.is_authenticated %}

... code to display game once user logs in ...

{% else %}
<img src="../static/assets/divinitypluslogo.png" width="130"></img>
<p>Your addictive web-based MMORPG</p>
<div class="container">
    <h2>Login</h2>
    <form method="POST" action="">
        {{ form.hidden_tag() }}
        {{ form.username }}
        {{ form.password }}
        {{ form.submit }}
    </form>
</div>
<form action="/register" method="POST">
    <p>Don't have an account? </p><a href="/register">Sign Up!</a>
</form>
{% endif %}
</body>

Where current_user is the current_user variable from the flask_login library. For additional information, the python code that handles this login looks like the following:

...

class LoginForm(FlaskForm):
    username = StringField(validators=[InputRequired(), Length(min=3, max=50)], render_kw={"placeholder": "Username"})
    password = PasswordField(validators=[InputRequired(), Length(min=4, max=50)], render_kw={"placeholder": "Password"})

submit = SubmitField("Login")

@login_manager.user_loader
def load_user(user_id):
    return User.query.get(int(user_id))

... some other pages ...

@app.route("/", methods=["POST", "GET"])
@app.route("/home", methods=["POST", "GET"])
def home():
    form = LoginForm()
    if form.validate_on_submit():
        user = User.query.filter_by(username=form.username.data).first()
        if user:
            if bcrypt.check_password_hash(user.password, form.password.data):
                login_user(user)

    # Messages (When adding a date/most recent, make sure to sort by that. For now, ignore time)
    messages = History.query.all()
    messages = list(reversed(messages))

    return render_template("home.html", form=form, current_user=current_user, messages=messages)

...