CSRF
What is CSRF?
Cross Site Request Forgery occurs when a malicious website contains a link, a form button or some JavaScript that is intended to perform some action on your website, using the credentials (or location on the network, not covered by this documentation) of a logged-in user who visits the malicious site in their browser.
How to protect against CSRF with Django Ninja
Use an authentication method not automatically embedded in the request
CSRF attacks rely on authentication methods that are automatically included in requests started from another site, like cookies or Basic access authentication.
Using an authentication method that does not automatically gets embedded, such as the Authorization: Bearer
header for exemple, mitigates this attack.
Use Django's built-in CSRF protection
In case you are using the default Django authentication, which uses cookies, you must also use the default Django CSRF protection.
By default, Django Ninja has CSRF protection turned OFF for all operations.
To turn it on you need to use the csrf
argument of the NinjaAPI class:
from ninja import NinjaAPI
api = NinjaAPI(csrf=True)
Warning: It is not secure to use API's with cookie-based authentication! (like CookieKey
, or django_auth
) when csrf is turned OFF.
Django Ninja will automatically enable csrf for Cookie based authentication
from ninja import NinjaAPI
from ninja.security import APIKeyCookie
class CookieAuth(APIKeyCookie):
def authenticate(self, request, key):
return key == "test"
api = NinjaAPI(auth=CookieAuth())
or django-auth based (which is inherited from cookie based auth):
from ninja import NinjaAPI
from ninja.security import django_auth
api = NinjaAPI(auth=django_auth)
Django ensure_csrf_cookie
decorator
You can use the Django ensure_csrf_cookie decorator on an unprotected route to make it include a Set-Cookie
header for the CSRF token. Note that:
- The route decorator must be executed before (i.e. above) the ensure_csrf_cookie decorator).
- You must csrf_exempt
that route.
- The ensure_csrf_cookie
decorator works only on a Django HttpResponse
and not also on a dict like most Django Ninja decorators.
from django.http import HttpResponse
from django.views.decorators.csrf import csrf_exempt, ensure_csrf_cookie
@api.post("/csrf")
@ensure_csrf_cookie
@csrf_exempt
def get_csrf_token(request):
return HttpResponse()
Set-Cookie
header from Django.
Frontend code
You may use the Using CSRF protection with AJAX and Setting the token on the AJAX request part of the How to use Django’s CSRF protection to know how to handle that CSRF protection token in your frontend code.
A word about CORS
You may want to set-up your frontend and API on different sites (in that case, you may check django-cors-headers). While not directly related to CSRF, CORS (Cross-Origin Resource Sharing) may help in case you are defining the CSRF cookie on another site than the frontend consuming it, as this is not allowed by default by the Same-origin policy. You may check the django-cors-headers README then.