Understanding CSRF Attacks and How Flask Protects You
CSRF (Cross-Site Request Forgery) tricks a user's browser into making unintended requests to your app. It's silent, devastating, and completely preventable.
How CSRF Works
- User logs in to
bank.com— their browser stores a session cookie - User visits attacker's page
evil.com evil.comhas a hidden form that POSTs tobank.com/transfer- The browser automatically sends the session cookie with the request
- The bank processes the transfer as if the user initiated it
The Attack in Code
<!-- evil.com/attack.html -->
<form action="https://bank.com/transfer" method="POST" id="f">
<input name="to" value="attacker-account">
<input name="amount" value="10000">
</form>
<script>document.getElementById('f').submit();</script>
The victim doesn't click anything — the page auto-submits.
Protection: CSRF Tokens
A CSRF token is a random, unpredictable value tied to the user's session. Include it in every form; verify it on every POST.
Generating Tokens
import secrets
import hmac
import hashlib
from flask import session
def generate_csrf_token() -> str:
if 'csrf_secret' not in session:
session['csrf_secret'] = secrets.token_hex(32)
token = secrets.token_hex(16)
sig = hmac.new(
session['csrf_secret'].encode(),
token.encode(),
hashlib.sha256
).hexdigest()
return f"{token}.{sig}"
def validate_csrf(token: str) -> bool:
if not token or '.' not in token:
return False
raw, sig = token.rsplit('.', 1)
expected = hmac.new(
session.get('csrf_secret', '').encode(),
raw.encode(),
hashlib.sha256
).hexdigest()
return hmac.compare_digest(sig, expected)
Adding to Forms
<form method="POST">
<input type="hidden" name="csrf_token" value="{{ csrf_token }}">
<!-- rest of form -->
</form>
Validating on POST
from flask import request, abort
def csrf_check():
token = request.form.get('csrf_token') or request.headers.get('X-CSRFToken')
if not validate_csrf(token):
abort(403)
@app.route('/transfer', methods=['POST'])
def transfer():
csrf_check()
# safe to process
...
SameSite Cookies (Defence in Depth)
app.config['SESSION_COOKIE_SAMESITE'] = 'Lax' # or 'Strict'
app.config['SESSION_COOKIE_HTTPONLY'] = True
app.config['SESSION_COOKIE_SECURE'] = True # HTTPS only
SameSite=Lax prevents the cookie from being sent on cross-site POST requests, which alone stops most CSRF attacks.
Double Submit Cookie Pattern
An alternative for stateless APIs: set a CSRF cookie client-side, read it in JavaScript, and send it as a header. The server checks that the header matches the cookie.
Security is layered — use CSRF tokens and SameSite cookies.
0 Comments
Join the conversation
No comments yet. Be the first!