-
Notifications
You must be signed in to change notification settings - Fork 2
Description
Code of Conduct
- I agree to follow Django's Code of Conduct
Feature Description
Django's CSRF protection is based on tokens. It works well but requires some effort from the developer to make it happen - passing around CSRF tokens in templates, form fields and HTTP headers.
Today, a more modern implementation can be achieved using Fetch Metadata and Origin request headers. Those headers are automatically sent by the browser. This is how the Go standard library implements CSRF protection. A very good resource about the implementation is available here.
This proposal is to implement the Go's stdlib CSRF protection approach on Django. It uses the Sec-Fetch-Site header with Origin as a fallback. Regarding browser compatibility, this covers all versions of Chrome and Safari ever, Firefox going back to 2019, Edge going back to 2018, and even IE 11. (source, caniuse).
The algorithm is described here.
Related Resources
https://web.dev/articles/fetch-metadata
https://words.filippo.io/csrf/
OWASP/CheatSheetSeries#1803
Problem
- Modernize Django's CSRF protection mechanism
- Reduces developer friction, as there's no need to implement token logic on the front-end
- Simpler logic on the backend
Request or proposal
proposal
Additional Details
Requests for something like this:
- https://chaos.social/@apollo13/115026470230309000
- https://code.djangoproject.com/ticket/31823
- https://discord.com/channels/856567261900832808/1432815378861527151 (some people on this thread)
- https://www.reddit.com/r/django/comments/1oihb4l/comment/nm1xdhe/
Implementation Suggestions
I already took a shot implementing this at the django-modern-csrf library. You can see the actual algorithm implementation here.
There are some considerations to discuss before implementing this in Django. The ones I can think of:
CSRF_TRUSTED_ORIGINS
The Sec-Fetch-Site header uses different values for same-site and same-origin. This conflicts with the current approach for trusted origins. For example, if CSRF_TRUSTED_ORIGINS is set to https://*.example.com, it won't be possible to deny same-site requests.
Probably a new setting will need to be created so the developer can pick the appropriate allowed values for the Sec-Fetch-Site header. Something like:
CSRF_SEC_FETCH_ALLOWED: Defines values allowed on the Sec-Fetch-Site header. Default: ['same-origin', 'none'].
Suggestions for the setting name are welcome
So if the developer wants to allow *.example.com, both settings would have to be set accordingly (_TRUSTED_ORIGINS and the new one).
HTTPS
The Sec-Fetch-Site header is only sent to trustworthy origins, i.e. HTTPS and localhost. As the Origin header serves as a fallback, it shouldn't be a concern - however it's good to have it documented.
@csrf_protect
The csrf_protect decorator currently wraps the CsrfViewMiddleware on the view. If the new approach is implemented as a separate middleware this will have to be updated.
Let the user choose the best protection strategy - and pick a default
I think developers should be able to pick the right protection strategy for their needs. Even though the modern approach will probably be enough for most users, there will be cases where the token approach is still preferred.
This could be either by having a separate middleware so users can pick the appropriate one on the MIDDLEWARE setting, or a new config can be introduced (something like CSRF_PROTECTION_STRATEGY = "modern" or "legacy") and then the appropriate algorithm is applied on the middleware.
Migration
How to migrate current codebases to the new approach? One way would be would be to introduce the new algorithm but developers would have to manually change it if they want. For new projects (via startproject) the modern approach could be the default?
Metadata
Metadata
Assignees
Labels
Type
Projects
Status