53👍
Here is a fully coded answer. The idea of the solution is:
- you have to first visit the login page with GET to get the cookies file generated,
- then parse the CSRF token out of the cookies file
- and do the login using a POST request, passing the data with
-d
.
Afterwards you can perform any request always using that CSRF token in the data ($DJANGO_TOKEN
) or with a custom X-CSRFToken
header. To log out simply delete the cookies file.
Note that you need a referer (-e
) to make Django’s CSRF checks happy.
LOGIN_URL=https://yourdjangowebsite.com/login/
YOUR_USER='username'
YOUR_PASS='password'
COOKIES=cookies.txt
CURL_BIN="curl -s -c $COOKIES -b $COOKIES -e $LOGIN_URL"
echo -n "Django Auth: get csrftoken ..."
$CURL_BIN $LOGIN_URL > /dev/null
DJANGO_TOKEN="csrfmiddlewaretoken=$(grep csrftoken $COOKIES | sed 's/^.*csrftoken\s*//')"
echo -n " perform login ..."
$CURL_BIN \
-d "$DJANGO_TOKEN&username=$YOUR_USER&password=$YOUR_PASS" \
-X POST $LOGIN_URL
echo -n " do something while logged in ..."
$CURL_BIN \
-d "$DJANGO_TOKEN&..." \
-X POST https://yourdjangowebsite.com/whatever/
echo " logout"
rm $COOKIES
I have a slightly more secure version of this code, which uses a file for submitting the POST data, as a Gist on GitHub: django-csrftoken-login-demo.bash
Interesting background reading on Django’s CSRF token is on docs.djangoproject.com.
3👍
Passing username:password in a curl request is only good for HTTP Authentication, which isn’t how most websites do auth these days. Instead, you’ll have to post to the login page, get the cookie, then pass it back when requesting your desired page.
- [Django]-Multiple pages using Reportlab – Django
- [Django]-How to loop through httprequest post variables in python
- [Django]-Remove leading and trailing slash / in python
2👍
Actually @Paterino answer is correct but it will not work on every implementation of sed. Instead sed 's/^.*csrftoken\s*//')
we can use sed 's/^.*csrftoken[[:blank:]]*//')
which is more old fashioned. MacOSXs curl doesn’t use escaping, so \n\t\s
don’t work at all.
- [Django]-Gunicorn + nginx: Server via socket or proxy?
- [Django]-Validate image size in django admin
- [Django]-How do I use a dictionary to update fields in Django models?
2👍
I’m using Django 4.1.2 and trying the @Paterino method found a couple of changes to make it work (but i have not enogh reputation to comment so wrote another answer).
Firstly, if the generated cookies.txt file is empty you have to ensure than csrf cookie is generated. I achieved this using django.views.decorators.csrf.ensure_csrf_cookie
in django.contrib.auth.views.LoginView
Now, after login cookies.txt changes, so you have to recalculate DJANGO_TOKEN variable in the same way:
DJANGO_TOKEN="csrfmiddlewaretoken=$(grep csrftoken $COOKIES | sed 's/^.*csrftoken\s*//')"
From here the method doesn’t change.
- [Django]-Django – CSRF verification failed
- [Django]-Django 2.0 path error ?: (2_0.W001) has a route that contains '(?P<', begins with a '^', or ends with a '$'
- [Django]-DRF: custom ordering on related serializers
1👍
To use the token with a get request, use
$CURL_BIN \
-H "$DJANGO_TOKEN" \
-X GET https://yourdjangowebsite.com/whatever/
I tried using -d with -X GET, however it resulted in weird socket behaviour on the server side (Heruko H18 errors).
- [Django]-Django form dropdown list of numbers
- [Django]-Django delete unused media files
- [Django]-Django site with 2 languages
0👍
the accepted answer, until now(2022-12-19), has 2 issues:
- misses updating
DJANGO_TOKEN
after login (since a newcsrftoken
cookie is returned after login) - doesn’t include an example with a POST request (moving the
csrftoken
to a header) where-d
already contains some payload
here is my version dealing with both:
# user and password from `./manage.py createsuperuser`
YOUR_USER='user'
YOUR_PASS='pass'
COOKIES=cookies.txt
LOGIN_URL=http://localhost:8000/admin/login/
# stores csrftoken cookie on cookies.txt
curl -s -c $COOKIES $LOGIN_URL > /dev/null
TOKEN_VALUE="$(grep -oP '(?<=csrftoken[[:space:]]).*' cookies.txt)" # https://stackoverflow.com/a/10358949/3026886 https://stackoverflow.com/a/4233691/3026886
# logs in, updating csrftoken and adding sessionid cookies
curl -b $COOKIES -c $COOKIES -d "csrfmiddlewaretoken=$TOKEN_VALUE&username=$YOUR_USER&password=$YOUR_PASS" $LOGIN_URL
# updates var env with new cookie
TOKEN_VALUE="$(grep -oP '(?<=csrftoken[[:space:]]).*' cookies.txt)"
# here comes the real request
curl -s -X POST -b $COOKIES -d "{\"a\":1}" -H "X-CSRFToken: $TOKEN_VALUE" http://localhost:8000/yourViewReceivingJsonPayload/ > /dev/null
rm cookies.txt
- [Django]-How to allow users to change their own passwords in Django?
- [Django]-How can I set the field unique in django?
- [Django]-Invalid command WSGIDaemonProcess Deploy Django application on CentOS 6.7
0👍
To sum up from https://stackoverflow.com/a/24376188/14298786 and https://stackoverflow.com/a/74186787/14298786 , use the following shell script:
# Example using curl to log in with an account.
LOGIN_URL=http://127.0.0.1:8000/accounts/login/ # change it to yours
PROTECTED_URL=http://127.0.0.1:8000/problem/reference/ # change it to yours
YOUR_USER=xxxx # change it to yours
YOUR_PASS=xxxx # change it to yours
COOKIES=cookies.txt
CURL_BIN="curl -s -c $COOKIES -b $COOKIES -e $LOGIN_URL"
echo "Django Auth: get csrftoken..."
$CURL_BIN $LOGIN_URL > /dev/null
DJANGO_TOKEN="csrfmiddlewaretoken=$(grep csrftoken $COOKIES | sed 's/^.*csrftoken[[:blank:]]*//')"
echo "Perform login..."
$CURL_BIN \
-d "$DJANGO_TOKEN&username=$YOUR_USER&password=$YOUR_PASS" \
-X POST $LOGIN_URL
echo "Visit a url that only authenticated users could access..."
# Recalculate token: https://stackoverflow.com/a/74186787/14298786
DJANGO_TOKEN="csrfmiddlewaretoken=$(grep csrftoken $COOKIES | sed 's/^.*csrftoken[[:blank:]]*//')"
$CURL_BIN \
-d "$DJANGO_TOKEN&..." \
-X POST $PROTECTED_URL
echo "logout"
rm $COOKIES
ps. Your login view should be something like:
def LoginView(request):
username = request.POST["username"]
password = request.POST["password"]
user = authenticate(request, username=username, password=password)
if user is not None:
login(request, user)
return JsonResponse(data={"successfully": "logged in"})
else:
return JsonResponse(data={"access": "denied"})
- [Django]-Redirect to same page after POST method using class based views
- [Django]-Cannot import name patterns
- [Django]-AttributeError: 'Settings' object has no attribute 'ROOT_URLCONF'