Broken Access Control, CSRF, and Session Management
Introduction
Broken access control is the number one risk in the OWASP Top 10 for good reason: apps check that you are logged in, but forget to check that you are allowed to see a particular record. Closely related are request forgery and weak session handling. This lesson shows how to design each defense correctly.
Prerequisites
- The IAM fundamentals lesson
- Basic web application knowledge
Step-by-Step Guide
Step 1: Recognize broken access control
The classic form is an insecure direct object reference: a URL such as invoices/1001 works, and changing it to invoices/1002 shows someone else's invoice, because the server never checked ownership. Other forms are reaching admin functions by guessing the URL, changing a role in a request, or relying on the user interface to hide options. Hiding a button is never access control.
Step 2: Enforce authorization on the server for every request
Deny by default, check authorization in one central, reusable place, and verify both the function and the specific object being accessed. Use unguessable identifiers as a helpful extra, but never as the protection itself. Log access control failures and alert on repeated ones.
@app.get("/invoices/<int:invoice_id>")
@login_required
def get_invoice(invoice_id):
invoice = db.get_invoice(invoice_id)
# Check ownership, not just that the user is logged in
if invoice is None or invoice.owner_id != current_user.id:
abort(404)
return jsonify(invoice.to_dict())Step 3: Understand CSRF
Cross-site request forgery tricks a logged-in user's browser into sending a state-changing request to your site from a malicious page, with the user's cookies attached. Use your framework's built-in CSRF protection first. For stateful apps, the synchronizer token pattern puts a secret, per-session token in forms or a custom header and verifies it on every state-changing request. For stateless apps, use a signed double-submit cookie bound to the session, since the naive version can be defeated. Never use GET for actions that change state.
Step 4: Add layers against CSRF
SameSite cookies, set to Lax or Strict, greatly reduce CSRF but should be used in addition to tokens, not instead. Checking the Sec-Fetch-Site header, with an Origin or Referer fallback, is a lightweight server-side check. Require re-authentication or confirmation for sensitive actions. Remember that cross-site scripting can defeat every CSRF defense, so fixing XSS is part of this fix.
Step 5: Manage sessions securely
Use your framework's session management rather than writing your own. Session identifiers must be generated with a secure random source and carry at least 64 bits of entropy, with 128 bits advisable if you build your own, and must contain no personal data. Send them only in cookies marked Secure, HttpOnly, and SameSite, never in URLs. Use a generic cookie name rather than the framework default.
Step 6: Handle login, timeout, and logout properly
Issue a new session identifier at login and whenever privileges change, and destroy the old one, which prevents session fixation. Apply idle and absolute timeouts that match the risk of the application. On logout, invalidate the session on the server, not just in the browser, and clear the cookie. Add Cache-Control no-store to sensitive responses so they are not kept in caches.
FAQs
- Why return 404 instead of 403 for someone else's record?
- It avoids confirming that the record exists. Either is acceptable, but a not-found response leaks less information to someone probing identifiers.
- Do I need CSRF protection for a pure JSON API using tokens in headers?
- APIs that authenticate with tokens sent in custom headers, not cookies, are generally not exposed to CSRF. If your API uses cookies for authentication, it needs the protections described here.
Summary
Check authorization on the server for every function and object, defend against CSRF with framework tokens plus SameSite cookies, and manage sessions with secure random IDs, secure cookies, regeneration at login, sensible timeouts, and real server-side logout.
Comments
Log in or create a free account to join the discussion.