Cookie-based sessions

Bocadillo has support for signed cookie-based HTTP sessions. These are available to use both in HTTP and WebSocket endpoints as a simple means of persisting data between requests. This data is stored in cookies that the client's web browser automatically sends back on subsequent requests.

Security considerations

When misused, cookies can be vectors for several attacks including Man-in-the-Middle, Cross-Site Scripting (XSS) and Cross-Site Request Forgery (CSRF).

The first one can be mitigated by using HTTPS. XSS is mostly mitigated by the httponly flag set by Bocadillo when setting up the cookie.

However, we do not provide any CSRF mitigation at the moment. For this reason, we strongly advise against using cookie-based sessions to store sensitive data, e.g. user credentials.

As an additional security measure, though, Bocadillo uses signed cookies to make sure that cookies only contain what you (i.e. the server-side application) have put in it. In other words, if the cookie content was changed by a malicious client, Bocadillo will tell and prevent it from being processed.

This is only possible thanks to the use of a secret key. As for anything secret, you should store it in a safe place. An environment variable is a good choice, and we make it easy to retrieve it from there, as described later.

Prerequisites

The sessions feature requires to install Bocadillo with the [sessions] extra:

pip install bocadillo[sessions]

Enabling and configuring sessions

To enable sessions, use the SESSIONS setting. It should at least contain a secret_key. Use Starlette's Secret datastructure to make sure the secret key is never displayed in plain text.

# myproject/settings.py
from starlette.config import Config
from starlette.datastructures import Secret

config = Config(".env")
SESSIONS = {"secret_key": config("SECRET_KEY", cast=Secret)}

You can use tools like the Django Secret Key Generator to generate strong enough secret keys.

Besides, you can declare any other option available in Starlette's SessionsMiddleware to fine-tune how sessions behave.

For example, pass "https_only": True to restrict cookie-based sessions to requests made over HTTPS:

# myproject/settings.py
SESSIONS = {
    # ...
    "https_only": True,
}

Using sessions

Once sessions are enabled and configured, use the req.session dictionary to retrieve and store session data.

In the following example, we use a cookie-based session to store the ID of the last todo item that was sent to the client, and only send the new todo items for future requests.

# todos/app.py
from bocadillo import App, configure
from . import settings

app = App()
configure(app, settings)


TODOS = [
    {"id": 0, "content": "Go shopping"},
    {"id": 1, "content": "Cook fries"},
    {"id": 2, "content": "Do laundry"},
]


@app.route("/unseen-todos")
async def get_unseen_todos(req, res):
    last_id = req.session.get("last_id", -1)
    unseen_todos = TODOS[last_id + 1 :]

    if unseen_todos:
        req.session["last_id"] = unseen_todos[-1]["id"]

    res.json = unseen_todos


@app.route("/todos", methods=["post"])
async def create_todo(req, res):
    json = await req.json()
    todo = {"id": len(TODOS) + 1, "content": json["content"]}
    TODOS.append(todo)
    res.status_code = 201

The SECRET_KEY environment variable must be set, so use the following to launch this example app:

SECRET_KEY=1234 uvicorn example.app:app