CSRF Protection
Buraq includes CSRF protection utilities to prevent cross-site request forgery attacks.
How it works
Section titled “How it works”The CSRF system stores a random token in the user session. On any state-changing request (POST, PUT, PATCH, DELETE), the submitted token is compared to the stored one. Mismatches return a 403 Forbidden.
get_token
Section titled “get_token”Return the CSRF token for the current request. Creates one if it doesn’t exist yet.
from buraq.contrib.csrf import get_token
async def my_view(request): token = get_token(request) return await render(request, "form.html", {"csrf_token": token})In templates, include the token as a hidden field:
<form method="post"> <input type="hidden" name="csrfmiddlewaretoken" value="{{ csrf_token }}"> ...</form>@csrf_protect
Section titled “@csrf_protect”Force CSRF validation on a specific view, regardless of middleware settings.
from buraq.contrib.csrf import csrf_protect
@csrf_protectasync def payment_view(request): ...Safe methods (GET, HEAD, OPTIONS, TRACE) always pass through without checking.
For AJAX requests, send the token in the X-CSRFToken header:
fetch("/api/submit", { method: "POST", headers: { "X-CSRFToken": getCookie("csrftoken") }, body: JSON.stringify(data),});@ensure_csrf_cookie
Section titled “@ensure_csrf_cookie”Set the CSRF cookie on the response even if the view doesn’t use the token directly. Useful for single-page apps that need the cookie before making their first POST request.
from buraq.contrib.csrf import ensure_csrf_cookie
@ensure_csrf_cookieasync def home(request): return await render(request, "home.html")CsrfViewMiddleware — stack-level CSRF protection
Section titled “CsrfViewMiddleware — stack-level CSRF protection”CsrfViewMiddleware is in the default MIDDLEWARE, so every unsafe request is
checked unless the view it reaches is decorated @csrf_exempt. Nothing needs
adding:
MIDDLEWARE = [ "buraq.middleware.security.SecurityMiddleware", "buraq.middleware.cors.CORSMiddleware", "buraq.contrib.sessions.middleware.SessionMiddleware", "buraq.contrib.auth.middleware.AuthenticationMiddleware", "buraq.middleware.csrf.CsrfViewMiddleware", "buraq.middleware.gzip.GZipMiddleware",]It reads the session, so it belongs below SessionMiddleware.
Why the token changes on every page
Section titled “Why the token changes on every page”The value in {{ csrf_input }} and the csrftoken cookie is different in every
response, even though the secret behind it does not change. That is deliberate.
Buraq compresses responses by default, and compression plus a secret that repeats in every response is the precondition for BREACH: an attacker who can get reflected input onto a page learns the secret a character at a time by watching how well the response compresses. A token that never repeats gives that attack nothing to measure.
The secret lives in the session; what is rendered is that secret combined with a fresh random mask, and what is submitted is unmasked before it is compared. Any token issued for the current session validates, however old.
The two halves of that are exported, for a client that has to mask or compare a token itself:
from buraq.contrib.csrf import mask_token, unmask_token
masked = mask_token(secret) # secret -> a value safe to put in a responsesecret = unmask_token(masked) # back again, before comparingunmask_token returns its argument unchanged when handed something that is not
masked, so a token from any source compares correctly.
How it works:
- Safe methods (
GET,HEAD,OPTIONS,TRACE) pass through unchecked. - Unsafe methods (
POST,PUT,PATCH,DELETE) must supply the CSRF token via:X-CSRFTokenrequest header, orcsrfmiddlewaretokenfield in the POST body.
- The middleware injects a
Set-Cookie: csrftoken=...header on every response so JavaScript clients can read the token from the cookie. - When reading the POST body to find the token, the middleware buffers and replays the body so the view still receives it intact.
// Read from cookie, send in headerfetch("/api/submit", { method: "POST", headers: { "X-CSRFToken": getCookie("csrftoken") }, body: JSON.stringify(data),});Use @csrf_protect for per-view protection when you prefer not to use the middleware globally.
@csrf_exempt
Section titled “@csrf_exempt”Skip CSRF validation for a specific view — typically used for webhooks from third parties.
from buraq.decorators import csrf_exempt
@csrf_exemptasync def stripe_webhook(request): payload = await request.json() ...Cookie and field names
Section titled “Cookie and field names”| Constant | Default value |
|---|---|
CSRF_COOKIE_NAME |
csrftoken |
CSRF_FIELD_NAME |
csrfmiddlewaretoken |
CSRF_HEADER_NAME |
x-csrftoken |
from buraq.contrib.csrf import CSRF_COOKIE_NAME, CSRF_FIELD_NAME