Published: January 03, 2025
Modified: January 03, 2025
Duration: 5 min
Words: 900
Django has built-in support for protection against CSRF by using a CSRF token.
It's enabled by default when you scaffold your project using the django-admin
startproject <project>
command, which adds a middleware in settings.py.
Every POST request to your Django app must contain a CSRF token. In a Django
template, you do this by adding {% csrf_token %}
to any form that uses the
POST method.
Let's see how that can be done with AJAX from a frontend that is separate from Django.
To show how it's done, we will build a simple app. In the backend, there is a URL of a picture; a GET request will get the picture and a POST request will set a new URL. The app has no error handling and such things in the frontend or backend for simplicity.
Right now it has two endpoints:
/get-picture
- gets the URL of an image stored in the server/set-picture
- sets the URL of an image stored in the serverAnd initially the backend code looks like this. I have written all the code in
urls.py
just to make everything simple.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
|
Now, for the frontend I'm showing you only the main functions.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
|
And for CORS reasons, you need to set up CORS in the backend, or else you can't make a request. Without CORS configuration in the backend, you will get an error like this.
1 |
|
To fix this, we have to add some headers to the responses. To do this, we will
use the django-cors-headers
package.
Install and configure django-cors-headers
package.
1 |
|
Configure in settings.py
1 2 3 4 5 6 7 8 9 10 11 |
|
Now everything for the GET request will work fine. But if you try to set the picture URL, in the backend you will see this error.
1 2 |
|
And this is what we will fix.
As your frontend is separate from Django, you need to manually ask for a CSRF
token. Django will send the token as a cookie by attaching it in the
Set-Cookie
headers in the response. The token will be saved in your browser
cookie named csrftoken
.
First add you frontend URL to CSRF_TRUSTED_ORIGINS
in settings.py
1 |
|
Create a new view to return the CSRF token as a cookie.
1 2 3 4 5 6 7 8 9 10 |
|
Now, we have a way to get the CSRF token from the backend. To, get the token in the frontend add the following code in your backend.
1 2 3 |
|
It makes a request to get a token. With credentials: "include"
it tells the
browser that if the response header contains any Set-Cookie
header, it should
obey that instruction. If you remove the credentails
property, the cookie
won't be set.
If you are curious, look at the network in browser console and check the headers. You will see a header similar to this.
1 |
|
Now the only thing we have to do is to send the CSRF token with the set-picture
POST call. Modify the set_picture
function.
1 2 3 4 5 6 7 8 9 10 |
|
We need to add an X-CSRFToken
header, and its value will be the value of the
csrftoken
cookie. To get the cookie, the js-cookie library has been used.
Now the POST request should work fine.
There may be other ways to do this. With this method, there are some cons you
need to be aware of. If you are deploying your frontend and backend on different
domains, there might be a problem with cookies due to browser security and
cookie policy. The browser might not set the CSRF cookie, because it will reject
any third-party cookie. And even if the security is low, you probably won't be
able to read the cookie value with Cookies.get("csrftoken")
due to policies
related to cookies.
Source Code: https://github.com/sujaudd1n/tutorials/tree/main/django-ajax-csrf