Cross-Site Request Forgery in Python
Prevention
Python does not provide built-in protection against CSRF attacks; the developer must by manually implement this by checking the anti-CSRF tokens or using one of the many, well-tested libraries and frameworks.
Flask
The following steps show how to manually protect a Flask endpoint /delete_user
from CSRF attacks using a per-session token generated at login.
-
Upon successful authentication, a random token is generated in a secure way and stored as a user’s session variable.
@login.route("/login", methods=['POST']) def login(): username = request.form.get("username") password = request.form.get("password") if validate_credentials(username, password): session['anti_crf_token'] = get_random_token() # ...
The function
get_random_token()
must return strong random string to be unique for the user’s session. Seeuuid
or Python3secrets
library to generate cryptographically secure tokens. -
To protect any state-changing endpoint, the forms must embed the anti-CSRF token as an hidden value. In the first snippet below, the
/users
endpoint renders theusers.html
Jinja2 template.@login.route("/users", methods=['GET']) def users(): if session_is_authenticated(): return render_template('users.html', anti_crf_token=session['anti_crf_token'])
The
users.html
template defines the HTML form containing the tokenanti_crf_token
.<form action="/delete_user" method="post"> <!-- ... --> <input type="hidden" name="anti_crf_token" value="{{anti_crf_token}}"> </form>
-
Now
/delete_user
can be secured by checking if theanti_csrf_token
sent in the form POST parameter matches thesession['anti_crf_token']
.@login.route("/delete_user", methods=['POST']) def delete_user(): anti_csrf_token = request.form.get("anti_csrf_token") if session['anti_crf_token'] != anti_csrf_token: return "Error, wrong anti CSRF token", 401 # Continue with a valid token
Other libraries can be used to automate part of the anti-CSRF protection, such as WTForms and Flask-WTF.
References
OWASP - Cross-Site Request Forgery Cheat Sheet MITRE - CWE 352 WTForms - Documentation Flask-WTF - CSRF Protection