Django clickjacking middleware and Firefox embedded PDFs - django

I recently added clickjacking protection to a Django app with django.middleware.clickjacking.XFrameOptionsMiddleware but found that PDFs no longer load in Firefox. While Chrome, Safari, and Edge embed the PDF as expected, Firefox throws the following console error:
Load denied by X-Frame-Options: <site_base>/<file_path>.pdf does not permit framing.
I found the #xframe_options_exempt decorator that I thought would help, but that just wraps a view whereas these are served directly through Apache. The only fix I've found is to get the "Ignore X-Frame-Options Header" browser plugin which isn't really a customer friendly solution.
I've tried using <embed>, <object>, and <iframe> to embed the PDF and all cause the same browser console error, and if I remove the clickjacking middleware the PDFs load just fine again.
Anyone run into something like this before? Relevant code:
<select id="pdf_graph_selection" onchange="updatePDF()">
{% for pdf_graph in pdf_graphs %}
<option value="{{pdf_graph.url"}}>{{pdf_graph.name}}</option>
{% endfor %}
</select>
<div id="pdf_container"></div>
function updatePDF() {
let new_source = $("#pdf_graph_selection").val();
let new_graph = "'<embed src='" + new_source + "' width='70%' height='900px' class='pdf_embed'></embed>;
$("#pdf_container").html(new_graph);
}

My coworker was able to help figure it out - We had a custom file response handler that must have been doing something Firefox didn't like. By adding the #xframe_options_sameorigin decorator it is now working.

Related

Forbidden (403) CSRF verification failed - Error with Docker, Django and Nginx

I am new to docker. Starting from a Django project (Django 4.0), I am using Docker to side by side with Nginx.
I used a docker-compose.yml file and used a custom configuration of Nginx, and everything works.
Only when I go to the login screen and click the "Login" button it comes up "Forbidden (403) CSRF verification failed. Request aborted.".
The code inside login.html is like this
<form method="post">{% csrf_token %}
{{ form|crispy }}
<button class="btn btn-success ml-2" type="submit">Log In</button>
Thanks in advance!
I would recommend you to read through all of these settings starting with "CSRF_" here
As you did not provide your settings.py I can only guess that the problem lays in there. Your form template is fine.
Probably my link leads you already to the correct setting, called CSRF_TRUSTED_ORIGINS where you basically input all your domains that you want to trust as a list. (Trust meaning which domain is allowed to send a post request)
settings.py:
CSRF_TRUSTED_ORIGINS = [
'https://trusted.domain.one.com',
'https://trusted.domain.two.com'
]
If this does not work try also to add the 'http://trusted.domain.one.com' without the S in httpS.

Django redirect page does not update the view

I'm using the Django Framework on Google App Engine.
I have multiple forms on the same view, to submit to different URL.
Trouble is after I get a form submitted: even if the called method update the datastore and some data, the previous page (where the forms are put in) is not refreshed, showing the updated data.
I could solve this problem using jQuery or some javascrip framework, appending dinamically content returned by the server but, how to avoid it?
Suggestions?
Am I wrong somewhere?
A part of "secure.html" template
<form action="/addMatch" method="post">
{% csrf_token %}
{{ form.as_p }}
<input type="submit" value="Submit" />
</form>
Matches:
<br />
{% for m in matches%}
{{m.description}} ---> {{m.reward}}
{% endfor%}
the "/addMatch" URL view:
def addMatch(request):
form = MatchForm(request.POST)
if form.is_valid():
user = User.all().filter('facebookId =', int(request.session["pbusr"]))
m = Match(user=user.get(),description =form.cleaned_data["description"],reward=form.cleaned_data["reward"])
m.save()
return HttpResponseRedirect("/secure/")
else:
logging.info("Not valid")
return HttpResponseRedirect("/secure")
The view method whose seems not working:
#auth_check_is_admin
def secure(request):
model={}
user = User.all().filter('facebookId =', int(request.session["pbusr"]))
u = user.get()
if (u.facebookFanPageId is not None and not u.facebookFanPageId == ""):
model["fanPageName"] = u.facebookFanPageName
model["form"] = MatchForm()
model["matches"] = u.matches
else:
....
return render(request,"secure.html",model)
Francesco
Based on what you posted, it seems like you're redirecting properly and are having database consistency issues. One way to test this would be to look at the network tab in the Google Chrome developer tools:
Click on the menu icon in the upper right
Click on "Tools"
Click on "Developer Tools"
Click on "Network" in the thing that opened up at the bottom of the screen.
Now, there will be a new entry in the network tab for every request that your browser sends and every response it receives. If you click on a request, you can see the data that was sent and received. If you need to see requests across different pages, you might want to check the "Preserve log" box.
With the network tab open, go to your page and submit the form. By looking at the network tab, you should be able to tell whether or not your browser issued a new GET request to the same URL. If there is a new request for the same page but that request has the old content, then you have a datastore consistency issue. If there was NOT a new request that yielded a response with the data for the page, then you have a redirect issue.
If it turns out that you have a datastore consistency issue, then what's happening is the data is being stored, but the next request for that data might still get the old data. To make sure that doesn't happen, you need what's called "strong consistency."
In a normal App Engine project, you get strong consistency by putting entities in the same entity-group and using ancestor queries. I'm not certain of what database/datastore you're using for Django and how the different database layers interact with App Engine's consistency, so this could be wrong, but if you can give your users the right key and then fetch them from that key directly (rather than getting all users and filtering them by key), you might get strong consistency.

User redirect with POST in iframe

I am building one of my first MVC 4 applications and I need some help with redirecting users.
I have a windows form application where I use a AxSHDocVw.AxWebBrowser to redirect the user to a specific URL , a SOAP web service to be precise, aswell as sending HTTP POST and HEADER data aswell.
This is done like so:
oHeaders = "Content-Type: application/x-www-form-urlencoded" + "\n" + "\r";
sPostData = "ExchangeSessionID=" + SessionID;
oPostData = ASCIIEncoding.ASCII.GetBytes(sPostData);
axWebBrowser2.Navigate2(ref oURL, ref o, ref o, ref oPostData, ref oHeaders);
I am looking to replicate this functionality in my MVC application, but am unsure of the how this can be done.
I was hoping to have this within an iframe, but can't find a way of sending the POST and HEADER data from this. This is what I have been trying so far:
Controller
ViewBag.URL = TempData["URL"];
ViewBag.SessionID = TempData["SessionID"];
ViewBag.FullURL = TempData["URL"] + "?ExchangeSessionID=" + TempData["SessionID"];
return View();
View
<iframe src="#ViewBag.FullURL" width="100%" height="500px"></iframe>
Basically I was trying to append the data to the end of the URL hoping this would work for the HTTP POST part. This is what I ended up with:
https://www.myurl.aspx?ExchangeSessionID=87689797
The user is being directed to the page, but the web service is giving me an error ( which tells me it is now receiving the POST data).
Can some please help me to try and fix this, or even give me advice on how to go about this another way. Like I said, I'm fairly new to MVC applications and I'm not entirely sure what I'm tryin to do is even possible.
Any help is appreciated. Thanks
I've decided to answer this question myself incase anybody is looking to do something similar in the future.
The first step was to create my iframe:
<iframe name="myframe" src="" width="100%" height="700px"></iframe>
Next I want to create a form with a button which, when pressed, will post the data to the url while targeting the iFrame (Note the target attribute of the form):
<form action="#ViewBag.URL" method="post" target="myframe">
<input type="hidden" name="ExchangeSessionID" value="#ViewBag.SessionID" />
<input type="submit" value="Submit" />
</form>
So what happens is, when the button is pressed, the form posts the ExchangeSessionID to the target URL and then the page response is displayed inside the iFrame.

How to block internet explorer on my Django web app?

I'm working on a web application that does not work well with Internet Explorer (web socket, json, security issues).
For now, before my application works with IE:
How can I refuse connections coming from Internet Explorer client ?
Thank you
Create a middleware where you're parsing the request.META['HTTP_USER_AGENT']. If you found that the user use IE, give him a nice message (e.g a notification or a little alert box) to says him that your site isn't optimized for his browser :)
Some code example: Django way
middleware.py (see the doc for more information)
class RequestMiddleware():
def process_request(self, request):
if request.META.has_key('HTTP_USER_AGENT'):
user_agent = request.META['HTTP_USER_AGENT'].lower()
if 'trident' in user_agent or 'msie' in user_agent:
request.is_IE = True
else:
request.is_IE = False
# or shortest way:
request.is_IE = ('trident' in user_agent) or ('msie' in user_agent)
your base template:
{% if request.is_IE %}<div class="alert">Watch out! You're using IE, but unfortunately, this website need HTML5 features...</div>{% endif %}
And then add it to your middlewares list.
Optimized: pure HTML way
If you only want to display a message like what I've done, you can use a HTML conditional comment:
<!--[if IE]> <div class="alert">...</div><![endif]-->
There is another way! Just use settings variable DISALLOWED_USER_AGENTS, and make sure that CommonMiddleware is installed on your site.
For example
import re
DISALLOWED_USER_AGENTS = (re.compile(r'msie\s*[2-7]', re.IGNORECASE), )
Cheers!

What is the difference between local and remote machine cookie delivery for the Django development server?

I have a Django 1.2.5 (stable release) site that is being developed and tested on a Ubuntu 10.10 machine. The dev box is on a LAN with some Windows machines that need to be able to view and test the site as well. So far, it is just the admin that I am trying to share.
The site works great on the dev box and can be run on 127.0.0.1:8080/admin or 192.168.17.165:8080/admin (the dev box's LAN IP) when starting the development server with the commands ./manage.py runserver and ./manage.py runserver 192.168.17.165:8080 respectively. However, when the windows machine tries to reach the server at 192.168.17.165:8080/admin the admin login page loads just fine, so things are almost set up correctly, but when the verified username and password are entered and the submit button is pressed, the post fails and the following error is returned:
Forbidden (403) CSRF verification failed. Request aborted.
Reason given for failure:
No CSRF or session cookie.
(very similar to #thomallen's post)
Here are some things that I have already checked that don't seem to have any effect:
I know that the login.html template has the {% csrf_token %} token added. When I view the source of the page, the tag
{% block content %}
{% if error_message %}
<p class="errornote">{{ error_message }}</p>
{% endif %}
<div id="content-main">
<form action="{{ app_path }}" method="post" id="login-form">{% csrf_token %}
<div class="form-row">
<label for="id_username">{% trans 'Username:' %}</label> <input type="text" name="username" id="id_username" />
</div>
is present. (stock admin login.html)
I have messed with the order of all the Middleware lines as seen here http://josh.gourneau.com/blog/2010/02/17/django-12-csrf-verification-failed/. Nothing.
Here is what I have:
MIDDLEWARE_CLASSES = (
'django.middleware.common.CommonMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.middleware.csrf.CsrfResponseMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware', )
I thought maybe I should mess with these but it didn't seem to help
SESSION_COOKIE_DOMAIN = '192.168.17.165'
CSRF_COOKIE_DOMAIN = '192.168.17.165'
Cleared out all other cookies and web history. Turned off firewall.
My current suspicion:
On the dev box (the one that works), Firefox shows 2 cookies present, sessionid and csrftoken. The windows boxes do not seem to be getting these tokens. I looked into the HTTP headers using the Firefox Live HTTP headers plugin on both machines. The linux box (same as server) gets:
Set-Cookie: csrftoken=83bae579460e9d123d9d904f4e2ef4d5; Max-Age=31449600; Path=/
Set-Cookie: sessionid=ec2b472837318347f6e6c8243f9e0afd; expires=Sat, 02-Apr-2011 04:29:46 GMT; Max-Age=1209600; Path=/
The windows box gets very similar info, but the Max-Age of the cookie is set to 0 (Max-Age=0). After this, the windows machines don't store the cookies. This apparently kills the session immediately according to ietf.org/rfc/rfc2109.txt. I did try setting SESSION_COOKIE_AGE to 10000 but it didn't take.
What do I need to do to get the stock Django development server to send valid cookies to a remote machine? I can't be the only one...
Other similar stackoverflow posts and links that seemed promising but didn't work:
Why is Django admin login giving me 403 CSRF error?
Django outputs CSRF token as object instead of value
I have read all the documentation and had a couple people help me look at it and I have no clue what could be causing this. I am sure there are ways to disable CSRF as a workaround, but shouldn't this be working?
It turned out to be a secondary piece of software that was not turned off. CA Anti Virus had a separate process running that was blocking cookies after the fact.