Django links and is_secure() - python

I am in the process of adding ssl to a django app and am running into some problems with my links redirecting from https to http.
Eventually I will have nginx redirect all http requests to https, but for now I am allowing both.
So if you go to https://www.mysite.com all is well. However, clicking on the "About link": href='/about' redirects you to http://www.mysite.com/about.
About is a super basic (static) page. Here's the entry in urls.py:
(r'^about/$', 'search.views.about'),
And here's the view function:
def about(request):
return render_to_response('search/about.html')
Shouldn't django direct my users to https://www.mysite.com/about/ ? I believe this has something to do with the request.is_secure() method. Which always returns false for me. I have read that I need to set the os.environ['HTTPS'] = "on" for it to return true. How can I go about doing this? Is there something else I need to do? Should I just hardcode https into all my links?
Thanks for all the help.

Turns out the nginx code I was using was screwing everything up. Instead of:
if ($http_x_forwarded_port != 443) { rewrite ^ https://$http_host/; }
use:
if ($http_x_forwarded_port != 443) { rewrite ^(.*) https://$host$1 permanent;
to do the redirection. As the first one drops all everything but the host.

Related

Using https as standard with django project

I am learning django and trying to complete my first webapp.
I am using shopify api & boilder plate (starter code) and am having an issue with the final step of auth.
Specifically, the redirect URL -- it's using HTTP:// when it should NOT and I don't know how to change it..
#in my view
def authenticate(request):
shop = request.GET.get('shop')
print('shop:', shop)
if shop:
scope = settings.SHOPIFY_API_SCOPE
redirect_uri = request.build_absolute_uri(reverse('shopify_app_finalize')) #try this with new store url?
print('redirect url', redirect_uri) # this equals http://myherokuapp.com/login/finalize/
permission_url = shopify.Session(shop.strip()).create_permission_url(scope, redirect_uri)
return redirect(permission_url)
return redirect(_return_address(request))
Which is a problem because my app uses the Embedded Shopify SDK which causes this error to occur at the point of this request
Refused to frame 'http://my.herokuapp.com/' because it violates the following Content Security Policy directive: "child-src 'self' https://* shopify-pos://*". Note that 'frame-src' was not explicitly set, so 'child-src' is used as a fallback.
How do i change the URL to use HTTPS?
Thank you so much in advance. Please let me know if I can share any other details but my code is practically identical to that starter code
This is what the Django doc says about build_absolute_uri:
Mixing HTTP and HTTPS on the same site is discouraged, therefore
build_absolute_uri() will always generate an absolute URI with the
same scheme the current request has. If you need to redirect users to
HTTPS, it’s best to let your Web server redirect all HTTP traffic to
HTTPS.
So you can do two things:
Make sure your site runs entirely on HTTPS (preferred option): Setup your web server to use HTTPS, see the Heroku documentation on how to do this. Django will automatically use HTTPS for request.build_absolute_uri if the incoming request is on HTTPS.
I'm not sure what gets passed in the shop parameter but if it contains personal data I'd suggest to use HTTPS anyway.
Create the URL yourself:
url = "https://{host}{path}".format(
host = request.get_host(),
path = reverse('shopify_app_finalize'))
But you will still need to configure your server to accept incoming HTTPS requests.

Change URL to another URL using mitmproxy

I am trying to redirect one page to another by using mitmproxy and Python. I can run my inline script together with mitmproxy without issues, but I am stuck when it comes to changing the URL to another URL. Like if I went to google.com it would redirect to stackoverflow.com
def response(context, flow):
print("DEBUG")
if flow.request.url.startswith("http://google.com/"):
print("It does contain it")
flow.request.url = "http://stackoverflow/"
This should in theory work. I see http://google.com/ in the GUI of mitmproxy (as GET) but the print("It does contain it") never gets fired.
When I try to just put flow.request.url = "http://stackoverflow.com" right under the print("DEBUG") it won't work neither.
What am I doing wrong? I have also tried if "google.com" in flow.request.url to check if the URL contains google.com but that won't work either.
Thanks
The following mitmproxy script will
Redirect requesto from mydomain.com to newsite.mydomain.com
Change the request method path (supposed to be something like /getjson? to a new one `/getxml
Change the destination host scheme
Change the destination server port
Overwrite the request header Host to pretend to be the origi
import mitmproxy
from mitmproxy.models import HTTPResponse
from netlib.http import Headers
def request(flow):
if flow.request.pretty_host.endswith("mydomain.com"):
mitmproxy.ctx.log( flow.request.path )
method = flow.request.path.split('/')[3].split('?')[0]
flow.request.host = "newsite.mydomain.com"
flow.request.port = 8181
flow.request.scheme = 'http'
if method == 'getjson':
flow.request.path=flow.request.path.replace(method,"getxml")
flow.request.headers["Host"] = "newsite.mydomain.com"
You can set .url attribute, which will update the underlying attributes. Looking at your code, your problem is that you change the URL in the response hook, after the request has been done. You need to change the URL in the request hook, so that the change is applied before requesting resources from the upstream server.
Setting the url attribute will not help you, as it is merely constructed from underlying data. [EDIT: I was wrong, see Maximilian’s answer. The rest of my answer should still work, though.]
Depending on what exactly you want to accomplish, there are two options.
(1) You can send an actual HTTP redirection response to the client. Assuming that the client understands HTTP redirections, it will submit a new request to the URL you give it.
from mitmproxy.models import HTTPResponse
from netlib.http import Headers
def request(context, flow):
if flow.request.host == 'google.com':
flow.reply(HTTPResponse('HTTP/1.1', 302, 'Found',
Headers(Location='http://stackoverflow.com/',
Content_Length='0'),
b''))
(2) You can silently route the same request to a different host. The client will not see this, it will assume that it’s still talking to google.com.
def request(context, flow):
if flow.request.url == 'http://google.com/accounts/':
flow.request.host = 'stackoverflow.com'
flow.request.path = '/users/'
These snippets were adapted from an example found in mitmproxy’s own GitHub repo. There are many more examples there.
For some reason, I can’t seem to make these snippets work for Firefox when used with TLS (https://), but maybe you don’t need that.

In Flask is there a way to ignore a request for a route that doesn't exist

I'm writing an app using Flask.
I have a set of routes and they work.
What I want to do on the client side is to ignore any requests to invalid URLs. That is I do not want to render any 404/error pages in the app. I would like an alert that says the URL is invalid and for the browser to simply stay on the same page.
I don't want to be checking the URLs in JavaScript on the client, as this would expose them.
I have a route which responds correctly to unknown URLs:
#app.errorhandler(404)
def non_existant_route(error):
return jsonify({"no":"such page"})
If I delete the return statement I get a 500 error.
I can't use abort()
Does this idea violate some HTTP principle?
Thanks
It sounds like you need a "catch-all" endpoint. Typically, it seems a catch-all endpoint would return a generic 404, but in your case, you probably want to return a 200 with some contextual information. Here's basically how you can do it (credit goes to http://flask.pocoo.org/snippets/57/):
from flask import Flask
app = Flask(__name__)
#app.route('/', defaults={'path': ''})
#app.route('/<path:path>')
def catch_all(path):
# returns a 200 (not a 404) with the following contents:
return 'your custom error content\n'
# This is just one of your other valid routes:
#app.route('/stuff')
def stuff():
return 'stuff\n'
if __name__ == '__main__':
app.run()
If you run this and curl various endpoints of the test app, here's what you get:
$ curl localhost:5000/stuff
stuff
$ curl localhost:5000/foo/bar
your custom error content
$ curl localhost:5000/otherstuff
your custom error content
As you can see, your other routes will still work as you expect.
I've decided a solution to this is too hard! I can not find any way to get the browser to ignore a response. There is no response header for 'do nothing'. If there was we would probably never see a webserver error again, which would not be good.
I could ajaxify all the requests as a way to grab the response headers and analyze them before any rendering or redirecting happens. That starts to break all the navigation (back buttons at least) and the pretty URLs. I could bung in a JS routing framework etc, and while I'm leaning how it works I'm not building my app (I already have enough to learn!)
#app.errorhandler(404)
def page_not_found(error):
return redirect(url_for('index'))
If you come up with something great post it anyway, I'm not the first to ask this question, and probably not the last.
Thanks
I remember reading about a javascript library some days ago (but I don't remember the name...). The clou with this library was, that all links and form submits were loaded not directly into the browser "_top" frame/window but into a hidden div and afterwards, when done, the content of the page was replaced by the content of this hidden div.
So if you want to catch bad links and such on client side you could hook up all links and submits and check the http response code. If it is not 200 (ok) you display an error. If it is okay you decide, if you replace the old page with the new content.
But there are two problems with this solution:
1. You would have to change the browsers location (in the address bar) without reloading the page of course!
2. It might get tricky to post some file uploads with javascript.
If I find the link or name of the js-library I saw, I will tell you!

Force Django to use HTTPS URLs when reversing

I am running my Django application in Cloud9 for development purposes using the usual ./manage.py runserver. But to the outside world, the app is accessible via a https:// URL.
The problem is that when I use the URL reverse function, the URLs that come back start with http:// (at least some of the time). When I try redirecting to one of those URLs, I get an error like this one in the console:
Mixed Content: The page at 'https://apps.facebook.com/xxxx/'
was loaded over HTTPS, but requested an insecure form action
'http://xxxx.c9users.io/facebook_app/gift_shop/'.
This request has been blocked; the content must be served over HTTPS.
My question: is there a way to force reverse to generate HTTPS URLs instead of HTTP?
Here is a snippet of code, which has problems with redirection from HTTPS URLs to HTTP ones:
class IndexRedirectView(RedirectView, CSRFExemptMixin):
permanent = False
def get_redirect_url(self, *args, **kwargs):
if self.request.user.visit_count >= 5:
return reverse('gift-shop')
if len(BaseGiftableInstance.objects.filter(giving_user=self.request.user)) > 0:
# has won something
return reverse('gift-shop')
return reverse('spinner')
If you are on Django 1.8 or greater, you can force SSL with the setting SECURE_SSL_REDIRECT = True - see this answer on a similar question
You should check out django-sslify, you just install it and add it to your MIDDLEWARE_CLASSES.

Allow hop-by-hop headers in Django proxy middleware

I need to implement a HTTP proxy in Django and my Google safari led me to a project called django-webproxy.
Although no longer maintained, it's quite simple. Most of the logic relies on a simple proxy Middleware class that intercepts all requests to the Django WSGI server and handles it.
If the Middleware returns any data, the WSGI server simply passes it back to the client but if it returns nothing, Django simply handles the request by passing to the other Middleware.
Everything works fine, pretty much, but I need to implement proxy authentication which mean i have to send a 407 status code to the client with a Proxy-Authenticate header. This sin't allowed by Django as it is a hop-by-hop header and Django throws an exception. How can i hack/force/kludge Django into allowing me to send hop-by-hop headers?
FYI, ihe code for the middleware class can be found here.
from django.core.servers import basehttp
del basehttp._hop_headers['proxy-authenticate']
del basehttp._hop_headers['proxy-authorization']
This worked for me.
django.core.servers.basehttp._hop_headers is no longer with us present in the basehttp module (since Django 1.10).
I know 2 ways to change it:
Start your server like so:
$ python -O ./manage.py runserver --noreload
Change wsgiref.util._hoppish:
import wsgiref.util
wsgiref.util._hoppish = {
'connection': 1, 'keep-alive':1,
'te':1, 'trailers':1, 'transfer-encoding':1,
'upgrade':1
}.__contains__

Categories