This project contains a Django web-application with multiple Cross-site Request Forgery (CSRF) vulnerabilities and utility scripts to exploit them. The goal of this project is to educate people about Cross-site Request Forgery.
This project was created for the DjangoCon EU 2022 talk Everything you didn't want to know about Cross-site Request Forgery (CSRF) in Django. You can find my blog post with furter information about the talk and a link to the recording here: My DjangoCon Europe 2022 Talk About Cross-site Request Forgery.
Disclaimer: Please don't use this code for any production system. It contains a lot of vulnerable pieces. It is configured this way for educational purposes.
This project was tested using Python 3.10.6. It should work with all Python versions supported by Django 4.1. I suggest to use pyenv to install and maintain Python versions. The backend resides in the vulnerable-backend
directory. The dependencies can be installed using poetry or the attached requirements.txt file (pip install -r requirements.txt
).
The db.sqlite3 file and media folder were added to this repository to offer a quick start.
The default credentials are:
- alice: csrf1234
- mallory: csrf1234
- admin: csrf1234 (If you need to log in to the Admin
http://localhost:8000/admin
)
- Start the vulnerable backend server (
vulnerable-backend
directory)python manage.py runserver 8000
- Start mallory's attack script
claim-best-sleeping-place.py
. - Start mallory's attack / ad server
cd attack-stage; python -m http.server --bind 0.0.0.0 9000
- Log in as alice
http://localhost:8000
(credentials above) - With the same browser you just logged in open
http://localhost:9000
and click on one of the "Buy Now" buttons to run the exploit. Check out theindex.html
file for more exploits and notes on how to run the exploit on page load. - Reset the attack by executing
python manage.py reset_stage
(inside thevulnerable-backend
directory)
If you wish to try this out cross-site, e.g. by setting configuring your /etc/hosts, you need to also disable SameSite protection for the session cookie by setting SESSION_COOKIE_SAMESITE = 'None'
. However, most modern browsers will require you to also set the secure attribute SESSION_COOKIE_SECURE=True
, thus making the local setup way more complicated, because a HTTPS connection is required for this to work. My advice would be to not set the secure flag and either test this with an old browser or to disable (some of) the protections of one of your browsers while testing.
For me, the following worked
- In Firefox 102 for macOS
- set
network.cookie.sameSite.noneRequiresSecure
tofalse
(about:config) - about:preferences#privacy > Enhanced Tracking Protection > Custom (disable cookie checkbox)
- set
- In the backend set
SESSION_COOKIE_SAMESITE = 'None'
.
Don't forget to undo the settings above after testing.
To try out the attacks using HTTP Basic Authentication instead of session cookies, you need to update vulnerable-backend/vulnerable_backend/settings.py
. Just comment out the HttpBasicAuthenticationMiddleware string in the MIDDLEWARE
variable. Afterwards HTTP Basic Authentication is required. Please do not use this middleware on production systems, it is just a quick hack to demonstrate that CSRF also works with HTTP Basic Authentication.
In my DjangoCon talk Everything you didn't want to know about Cross-site Request Forgery (CSRF) in Django I explain CSRF and Prevention mechanisms in detail.
- https://unsplash.com/photos/48bZh32St1Q
- https://unsplash.com/photos/ZuL8zFse-KY
- https://unsplash.com/photos/uy5t-CJuIK4
- https://unsplash.com/photos/e1yyDdjBEbM
- https://unsplash.com/photos/rUxW50rlAE8
- https://unsplash.com/photos/jIQ4Gzn8rzI
- https://unsplash.com/photos/R9NCAfvC6r0
- https://unsplash.com/photos/JzXbWRfh_gQ
- https://unsplash.com/photos/My4pywVClEk