Link Search Menu Expand Document

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.

  1. 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. See uuid or Python3 secrets library to generate cryptographically secure tokens.

  2. 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 the users.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 token anti_crf_token.

    <form action="/delete_user" method="post">
      <!-- ... -->
      <input type="hidden" name="anti_crf_token" value="{{anti_crf_token}}">
     </form>
    
  3. Now /delete_user can be secured by checking if the anti_csrf_token sent in the form POST parameter matches the session['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