CSRF Security Overview
In this page:
What CSRF Protection Prevents
Without protection, a malicious website could embed a hidden form that submits to your site using the victim's existing login session, performing actions the victim never agreed to. Django's CSRF token ties every form submission to the page that legitimately rendered it.
Note: CSRF protection only matters for state-changing requests like POST, not for read-only GET requests.
Example: What CSRF Protection Prevents
Without protection, a malicious website could embed a hidden form that submits to your site using the victim's existing login session, performing actions the victim never agreed to. Django's CSRF token ties every form submission to the page that legitimately rendered it.
<!-- templates/donate.html -->
<form method="post">
{% csrf_token %}
<label for="amount">Amount</label>
<input type="number" id="amount" name="amount">
<button type="submit">Donate</button>
</form>
{# Django-only code -- models.py/views.py/urls.py/settings.py
snippets, or template markup using Django template tags/variables
-- can't run standalone via Judge0 or the browser preview, since
it needs a real Django project. Only this course's pure-Python
examples (example_lang == 'python', no Django imports) are
actually runnable, so those still get the button below. #}
How the Middleware Checks It
django.middleware.csrf.CsrfViewMiddleware is enabled by default in settings.py. On every POST request, it compares the token submitted in the form against the one stored in the user's session/cookie, rejecting the request with a 403 if they don't match.
Warning: The middleware must stay in MIDDLEWARE for CSRF checks to run at all.
Example: How the Middleware Checks It
django.middleware.csrf.CsrfViewMiddleware is enabled by default in settings.py. On every POST request, it compares the token submitted in the form against the one stored in the user's session/cookie, rejecting the request with a 403 if they don't match.
# settings.py -- CSRF middleware is on by default
MIDDLEWARE = [
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
]
{# Django-only code -- models.py/views.py/urls.py/settings.py
snippets, or template markup using Django template tags/variables
-- can't run standalone via Judge0 or the browser preview, since
it needs a real Django project. Only this course's pure-Python
examples (example_lang == 'python', no Django imports) are
actually runnable, so those still get the button below. #}
Sending the Token from JavaScript
When submitting a POST request via fetch() or AJAX instead of a plain HTML form, the CSRF token must be read from the cookie and sent as a request header.
Example: Sending the Token from JavaScript
When submitting a POST request via fetch() or AJAX instead of a plain HTML form, the CSRF token must be read from the cookie and sent as a request header.
// Read the csrftoken cookie Django sets automatically
function getCookie(name) {
const match = document.cookie.match('(^|;)\\s*' + name + '\\s*=\\s*([^;]+)');
return match ? match.pop() : '';
}
fetch('/donate/', {
method: 'POST',
headers: { 'X-CSRFToken': getCookie('csrftoken') },
body: new URLSearchParams({ amount: 10 }),
});
{# Django-only code -- models.py/views.py/urls.py/settings.py
snippets, or template markup using Django template tags/variables
-- can't run standalone via Judge0 or the browser preview, since
it needs a real Django project. Only this course's pure-Python
examples (example_lang == 'python', no Django imports) are
actually runnable, so those still get the button below. #}
- Removing {% csrf_token %} from a form to fix a confusing 403 error instead of understanding why it's required.
- Using @csrf_exempt on a view just to make an error go away, opening it up to cross-site attacks.
- Forgetting that AJAX POST requests also need the CSRF token sent as a header, not just in HTML forms.
- CSRF (Cross-Site Request Forgery) tricks a logged-in user's browser into submitting a request they didn't intend to make.
- Django's CsrfViewMiddleware checks for a valid token on every unsafe request (POST, PUT, DELETE).
- Every form that submits with POST must include {% csrf_token %} inside the <form> tag.
- The token proves the request actually came from your site's own page, not an attacker's page.
Chapter Quiz — Complete all 9 topics to unlock
0/9 topics done
Complete these topics first: