CSRF protection (bottle_utils.csrf)

This module contains decorators and functions for facilitating CSRF protection.

App configuration

Functions in this module require the Bottle application to be configured with CSRF-specific options.

Here is an example of file-based configuration:

[csrf]
secret = SomeSecretValue
token_name = _csrf_token
path = /
expires = 600

secret setting is the only setting you really must override. Not having this setting set will result in KeyError exception.

When using dict-based configuration, prefix each key with csrf..

The keys have following meaning:

  • csrf.secret setting is a secret key used for setting cookies; it should be fairly random and difficult to guess
  • csrf.token_name setting is the name of the cookie and form field that contain the token
  • csrf.path setting is the path of the cookie
  • csrf.expires setting is in seconds and sets the cookie’s max-age

Caveat

As with most common CSRF protection schemes, decorators in this module will prevent the user from opening two forms and submitting them one after the other. This also applies to cases where forms are fetched from server side using XHR.

Every form must have a token, and a token must match the one in the cookie. However, there is only one cookie for the whole site. When you submit a form, the token in the cookie is replaced with a new one, making tokens in any of the previously opened forms invalid. The result is that form submission results in a HTTP 403 response (not authorized).

You need to decide whether you can live with this behavior before using this module. In case of XHR, consolidating different forms into a single form or fetching tokens separately may be viable solutions.

Note

A possible workaround for applications loading forms (and tokens) using XHR would be to reload all tokens on the page whenever one of the forms is submitted.

Functions and decorators

bottle_utils.csrf.csrf_protect(func)[source]

Perform CSRF protection checks. Performs checks to determine if submitted form data matches the token in the cookie. It is assumed that the GET request handler successfully set the token for the request and that the form was instrumented with a CSRF token field. Use the csrf_token() decorator to do this.

If the handler function returns (i.e., it is not interrupted with bottle.abort(), bottle.redirect(), and similar functions that throw an exception, a new token is set and response is returned to the requester. It is therefore recommended to perform a reidrect on successful POST.

Generally, the handler does not need to do anything CSRF-protection-specific. All it needs is the decorator:

@app.post('/')
@bottle.view('myform')
@csrf_protect
def protected_post_handler():
    if successful:
        redirect('/someplace')
    return dict(errors="There were some errors")
bottle_utils.csrf.csrf_tag()[source]

Generte HTML for hidden form field. This is a convenience function to generate a simple hidden input field. It does not accept any arguments since it uses the bottle.request object to obtain the token.

If the handler in which this function is invoked is not decorated with csrf_token(), an AttributeError will be raised.

Returns:HTML markup for hidden CSRF token field
bottle_utils.csrf.csrf_token(func)[source]

Create and set CSRF token in preparation for subsequent POST request. This decorator is used to set the token. It also sets the 'Cache-Control' header in order to prevent caching of the page on which the token appears.

When an existing token cookie is found, it is reused. The existing token is reset so that the expiration time is extended each time it is reused.

The POST handler must use the csrf_protect() decotrator for the token to be used in any way.

The token is available in the bottle.request object as csrf_token attribute:

@app.get('/')
@bottle.view('myform')
@csrf_token
def put_token_in_form():
    return dict(token=request.csrf_token)

In a view, you can render this token as a hidden field inside the form. The hidden field must have a name _csrf_token:

<form method="POST">
    <input type="hidden" name="_csrf_token" value="{{ token }}">
    ....
</form>
bottle_utils.csrf.generate_csrf_token()[source]

Generate and set new CSRF token in cookie. The generated token is set to request.csrf_token attribute for easier access by other functions.

It is generally not necessary to use this function directly.

Warning

This function uses os.urandom() call to obtain 8 random bytes when generating the token. It is possible to deplete the randomness pool and make the random token predictable.