minetest / contentdb

A content database for Minetest mods, games, and more
https://content.minetest.net
GNU Affero General Public License v3.0
93 stars 45 forks source link

CSRF swallows form submissions #437

Closed rubenwardy closed 2 weeks ago

rubenwardy commented 1 year ago

If you take a long time to write a review, thread, or comment, the content can be swallowed by CSRF. (Apparently submitting a form invalidates all CSRF tokens?)

Instead, the user should be returned to the form with an error saying to resubmit

sda97ghb commented 9 months ago

I investigated the topic and here what I found.

In the current state of the project, all CSRF protection is managed by flask_wtf.csrf.CSRFProtect.

flask_wtf.csrf.CSRFProtect sets up the template function csrf_token which is an alias for flask_wtf.csrf.generate_csrf.

flask_wtf.csrf.CSRFProtect also sets up a middleware (with @app.before_request), which extracts CSRF token from the request (from form data or HTTP header) and calls flask_wtf.csrf.validate_csrf.

flask_wtf.csrf.generate_csrf does the following:

  1. create a random value and save it as session["csrf_token"], if the value was not previously set

  2. sign this value with WTF_CSRF_SECRET_KEY and current time and return the result from the function

flask_wtf.csrf.validate_csrf does the following:

  1. check if the token from the request is not empty and session["csrf_token"] is set

  2. check if the token signed with correct signature and the signature is not older than WTF_CSRF_TIME_LIMIT

  3. check if the token and session["csrf_token"] are equal

This means that there is no such thing as invalidation of CSRF tokens. Tokens when created are signed with current time, and when checked can be considered expired if the signature is too old.

In fact flask_wtf.csrf implements Signed Double Submit Cookie technique with HMAC CSRF Token, recommended by OWASP Cheat Sheet Series (link). This cheat sheet also states that including timestamps in CSRF token is a common misconception, and CSRF tokens do not need to expire.

WTF_CSRF_TIME_LIMIT = None config parameter can be used to disable CSRF token expiration. documentation