CSRF in Flask Applications
Flask provides no built-in CSRF protection. Applications must use Flask-WTF or implement manual token validation. Many Flask tutorials skip CSRF entirely, leaving form submissions and AJAX requests vulnerable to cross-origin forgery attacks.
Scan Your Flask AppHow CSRF Manifests in Flask
Flask apps using flask-login or session-based auth are CSRF-vulnerable by default. Without Flask-WTF or manual token validation, any form submission from any origin will be accepted as long as the session cookie is present. Flask API endpoints that accept JSON are slightly protected because cross-origin JSON requests trigger CORS preflight. However, form-encoded POST requests bypass CORS entirely.
Real-World Impact
A Flask application for managing IoT devices had no CSRF protection. An attacker created a webpage that, when visited by an authenticated user, submitted requests to disable security cameras, unlock doors, and change admin passwords - all through hidden form submissions.
Step-by-Step Fix
Install and configure Flask-WTF
Flask-WTF provides CSRF protection for Flask forms.
from flask import Flask
from flask_wtf.csrf import CSRFProtect
app = Flask(__name__)
app.config['SECRET_KEY'] = 'your-secret-key'
csrf = CSRFProtect(app)
# In templates, include the CSRF token
# <form method="post">
# <input type="hidden" name="csrf_token" value="{{ csrf_token() }}">
# ...
# </form>Configure CSRF for AJAX requests
Send the CSRF token in a header for JavaScript requests.
<!-- In your base template -->
<meta name="csrf-token" content="{{ csrf_token() }}">
<script>
const csrfToken = document.querySelector('meta[name="csrf-token"]').content;
fetch('/api/update', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-CSRFToken': csrfToken,
},
body: JSON.stringify(data),
});
</script>Set SameSite on session cookies
Configure Flask session cookies with SameSite attribute.
app.config.update(
SESSION_COOKIE_SECURE=True,
SESSION_COOKIE_HTTPONLY=True,
SESSION_COOKIE_SAMESITE='Lax',
)Prevention Best Practices
1. Use Flask-WTF which provides CSRF protection for all forms. 2. Enable CSRFProtect globally for all POST/PUT/DELETE requests. 3. Set SameSite=Lax on session cookies. 4. Validate Origin headers on mutation endpoints. 5. For AJAX, send the CSRF token in a custom header.
How to Test
1. Create a cross-origin HTML form that POSTs to your Flask endpoints. 2. Check if forms include a csrf_token hidden field. 3. Submit a form without the token and verify it is rejected. 4. Verify session cookies have SameSite attribute. 5. Use Vibe App Scanner to detect missing CSRF protections in your Flask application.
Frequently Asked Questions
Does Flask have built-in CSRF protection?
No. Flask has no built-in CSRF protection. You must use Flask-WTF (CSRFProtect) or implement your own token validation. Without this, all cookie-authenticated endpoints are vulnerable to CSRF.
Does CSRFProtect work with REST APIs?
Yes. Flask-WTF CSRFProtect can protect API endpoints by requiring the X-CSRFToken header. For APIs that use only Bearer token authentication (no cookies), CSRF protection is not needed.
Can I exempt specific routes from CSRF?
Yes. Use @csrf.exempt on routes that have their own authentication (like webhooks with signature verification). Never exempt routes that rely on cookie-based session authentication.
Related Security Resources
Is Your Flask App Vulnerable to CSRF?
VAS automatically scans for csrf vulnerabilities in Flask applications and provides step-by-step remediation guidance with code examples.
Scans from $5, results in minutes. Get actionable fixes tailored to your Flask stack.