CRSF cookie not set in iframed Django View within another site - django

I have a Django app with about a dozen views that I am currently hosting on Heroku. I can do POST requests just fine to the app when directly going to the app url, and I have the 'django.middleware.csrf.CsrfViewMiddleware' enabled. I am running Django 2.1
I am currently having an issue where I am trying to embed this Django app within an iframe, on another site that I host on Weebly. I always get a 403 error when trying to do a post on any of the Django forms. The reason is "CSRF cookie not set."
I am doing this through Chrome on Ubuntu. I checked the Applications tab in the Developer console, and do see the csrftoken key-value pair set in the cookie for the Heroku domain. The Weebly domain does not contain the csrftoken key-value pair. I figured it would just use the cookie from the Heroku app domain and use the csrftoken, but that doesn't appear to be the case.
In Django, here are my settings regarding CSRF:
CSRF_COOKIE_SECURE = False
CSRF_TRUSTED_ORIGINS = ['example123.herokuapp.com',
'app123.weebly.com']
I REALLY don't want to disable security or use the csrf_exempt decorator, as that feels like a hack. I am pulling my hair out on this one!
EDIT:
{% csrf_token %} is in the form, and I can see the hidden field "csrfmiddlewaretoken":
<input type="hidden" name="csrfmiddlewaretoken" value="XXXXXXXXXXXXXXXXXXXXXXywkFTfTC9ttYiOTD0O8uF49SvRjaUWgWeLU0h2PjP2">

There are two different things with csrf in django
1. Csrfmiddlewaretoken : {% csrf_token %}
example of set-token header
2. CSRFcookie : I don't think that you did this one.
example of same request giving different csrf-token
here the images shown are both the examples of one of my app for a specific request
We do often confuse second with the first one. In the second case, the server sets a cookie in the first get request with a csrf token (this is a cookie and not the csrfmiddlewaretoken ), it needs to be sent every-time for csrf cookie verification. This is done by the browser itself and we mostly don't notice it. However the problem arises with using CORS (different origins of request like android/angular app etc).

Related

Prevent form resubmission with user refresh and need to submit data to webpage using Django

I've seen this question a few times and can't find an option that works in my situation.
I have a webpage that you can get to via a POST. It requires an 'example_id' be sent to the server.
The webpage has a form for the user to fill out. When they submit the form, I need the user to return to same page.
I need to prevent user 'refresh' from resubmitting the form. Most common solution I have found is:
return HttpResponseRedirect('/<web_page/')
I have tried this and adding kargs to the function parameters, but it doesn't seem to work.
Update:
I got my scenario to work by using the csrf token ({% csrf_token %}) . If the user refreshes the page, then the csrf token will be the same. So I check for this in my view and handle this scenario differently.
The solution I found.
I got my scenario to work by using the csrf token: {% csrf_token %}
If the user refreshes the page, then the csrf token will be the same. So I check for this in my view and handle this scenario differently than I would normally.

django CSRF token cookie not set for some users

I have been getting sporadic CSRF errors in an app that is mostly working ok. I do everything as I'm supposed to do: I use {% csrf_token %} in my template for normal forms and in my ajax POSTs I set the X-CSRFToken header:
$.ajaxSetup({
beforeSend: function(xhr, settings) {
xhr.setRequestHeader("X-CSRFToken", $.cookie('csrftoken'));
},
});
I'm even forcing the cookie to be set in all views by writing a custom Middleware that calls get_token
def CSRFForceCookieMiddleware(get_response):
def middleware(request):
response = get_response(request)
get_token(request) # Force to set cookie in all responses
return response
return middleware
Everything works OK in my localhost and in production for most users. But for some users I get 403 CSRF validation error.
I added a lot of debug info. Turns out that even if CsrfViewMiddleware is setting the csrftoken is setting the cookie in the response, in the actual browser the cookie is not set ($.cookie('csrftoken') is null). So when the ajax call is made, there is no cookie present in the request.
So, I guess this pretty much means that some users' browsers are blocking this cookie? Anyone else had this experience?
Most browsers have an option to "block all cookies". You may want to detect that in javascript and give your users a warning that some functional cookies are required for the site to work correctly. There's another SO question that shows how to do that.
Alternatively, grab the token from a hidden input field ({% csrf_token %} will add that field in your template). That should always work.

What's the relationship between csrfmiddlewaretoken and csrftoken?

I was working with Django to create a website and got some questions about CSRF. I use django.middleware.csrf.CsrfViewMiddleware and add <form action="" method="post">{% csrf_token %} in my POST form.
And when I test the website:
GET / HTTP/1.1
Host: 123.207.137.168:8000
Then, I got cookies
Set-Cookie:csrftoken=Ev8veOH89vFDnG3a0GJUsMXA1oGZXxqXRw2nFWiKrvZ9UE10niTlZCiOxdnoKfTv; expires=Thu, 27-Dec-2018 06:37:41 GMT; Max-Age=31449600; Path=/
But in the html:
<input type='hidden' name='csrfmiddlewaretoken' value='JswHLk4fNpxHkh0OObD1uKiOxSDUzkMDWtqzcsFR5pRdRfYEbNNs1AD23Hkjm2fb' />
So I was wondering why the csrftoken and csrfmiddlewaretoken is different and how did the server use these two value to valid if the request is from the users or hackers?
There are some answers in the docs, but looking at the code really solves this "mystery"
Basically what django does is the following:
Craft a random CSRF secret key
Create a random mask string (previously incorrectly called "salt"). Using the mask string, mask (it's not a proper encryption hence the name "mask") the secret key and set the masked result as a CSRF cookie named csrftoken (after version 4.1 this step is skipped and the csrftoken will NOT be masked)
When user opens a form/request/anything, check if the user has the CSRF cookie set (if they don't have it, craft it as above). If they have it, fetch it, unmask it and get the real secret, re-mask it (with another random mask string) and use this as another token (csrfmiddlewaretoken).
Now when you make a POST request for example, the following happens
You send the csrfmiddlewaretoken
Django unmasks the csrf cookie (csrftoken) (after version 4.1 csrf cookie is not masked so it just gets the value)
Django unmasks the token you sent (csrfmiddlewaretoken)
Django compares them. If the two match, you're ok.
This method with the two tokens is called Double-Submit Cookie. Django's way with the masking allows to keep the same csrf secret for some time without having to renew the key for every request

Why is Django csrf_token permanent

From the documentation, I found django use a permanent, not session based csrf cookie. But if I look at the source html of the web page in which a form locates, I can see the hidden input which means I can get csrf cookie. If the cookie is permanent, I can forge a POST request with the cookie. Is that it?
It is changed after login/logout. You can check it with JQuery by getting in from cookie:
document.cookie.match(/csrftoken=([\w]+)/)
And try to compare it after relogin.

Csrf token verification fails between two Django web applications

I am trying to pass csrf token between two web application to make one POST data to the other.
"client" application (C) asks csrf token to "server" application (S) via a GET operation.
S responds to C with a form:
<form id='csrfRequestForm' name='csrfForm' action='http://{{ context_path }}/ajax/getcsrf' method='post'>
<!-- csrf token -->
{% csrf_token %}
<!-- datas to POST follow -->
...
</form>
C has to submit this form to action (mapped on a url used by S) in order to POST datas to S.
When C tries to do it, csrf verification fails. I've checked GET's result and csrf token is received with the form. I have django.middleware.csrf.CsrfViewMiddleware keyword listed under MIDDLEWARE CLASSES in settings.py and RequestContext is passed when rendering form's view with render_to_response(... RequestContext(request))
What am I doing wrong?
Thanks
Try defining your context and returning it like this...
context = RequestContext(request, {
'request': request
})
return render_to_response(..., context_instance=context)
This is by design, and disallows for cross site POST execution. One option you have is to mark the methods you would like to be able to execute as safe, as per the django docs:
https://docs.djangoproject.com/en/dev/ref/contrib/csrf/
I wasn't able to resolve it in your way, but I managed out how to do it:
C go directly to S via javascript opening a popup with:
window.open("http://<S_address>/<path_to_request_form>");
In this way, user using C that is logged via a third party authentication server (I forgot to mention it earlier, sorry), is still logged in the popup window in S and receives the form in it with a correct csrf token. I don't know if it's correct but it works.
Thanks for your time