- Author:
- sjzabel
- Posted:
- March 6, 2007
- Language:
- Python
- Version:
- Pre .96
- Score:
- 17 (after 17 ratings)
SSL Middleware
This middleware answers the problem of redirecting to (and from) a SSL secured path by stating what paths should be secured in urls.py file. To secure a path, add the additional view_kwarg 'SSL':True to the view_kwargs.
For example
urlpatterns = patterns('some_site.some_app.views',
(r'^test/secure/$','test_secure',{'SSL':True}),
)
All paths where 'SSL':False or where the kwarg of 'SSL' is not specified are routed to an unsecure path.
For example
urlpatterns = patterns('some_site.some_app.views',
(r'^test/unsecure1/$','test_unsecure',{'SSL':False}),
(r'^test/unsecure2/$','test_unsecure'),
)
Gotcha's Redirects should only occur during GETs; this is due to the fact that POST data will get lost in the redirect.
Benefits/Reasoning
A major benefit of this approach is that it allows you to secure django.contrib views and generic views without having to modify the base code or wrapping the view.
This method is also better than the two alternative approaches of adding to the settings file or using a decorator.
It is better than the tactic of creating a list of paths to secure in the settings file, because you DRY. You are also not forced to consider all paths in a single location. Instead you can address the security of a path in the urls file that it is resolved in.
It is better than the tactic of using a @secure or @unsecure decorator, because it prevents decorator build up on your view methods. Having a bunch of decorators makes views cumbersome to read and looks pretty redundant. Also because the all views pass through the middleware you can specify the only secure paths and the remaining paths can be assumed to be unsecure and handled by the middleware.
This package is inspired by Antonio Cavedoni's SSL Middleware
Notes: Updated per Jay Parlar at http://www.djangosnippets.org/snippets/240/ - Added a test for the way webfaction handles forwarded SSL requests.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 | __license__ = "Python"
__copyright__ = "Copyright (C) 2007, Stephen Zabel"
__author__ = "Stephen Zabel - [email protected]"
__contributors__ = "Jay Parlar - [email protected]"
from django.conf import settings
from django.http import HttpResponseRedirect, HttpResponsePermanentRedirect, get_host
SSL = 'SSL'
class SSLRedirect:
def process_view(self, request, view_func, view_args, view_kwargs):
if SSL in view_kwargs:
secure = view_kwargs[SSL]
del view_kwargs[SSL]
else:
secure = False
if not secure == self._is_secure(request):
return self._redirect(request, secure)
def _is_secure(self, request):
if request.is_secure():
return True
#Handle the Webfaction case until this gets resolved in the request.is_secure()
if 'HTTP_X_FORWARDED_SSL' in request.META:
return request.META['HTTP_X_FORWARDED_SSL'] == 'on'
return False
def _redirect(self, request, secure):
protocol = secure and "https" or "http"
newurl = "%s://%s%s" % (protocol,get_host(request),request.get_full_path())
if settings.DEBUG and request.method == 'POST':
raise RuntimeError, \
"""Django can't perform a SSL redirect while maintaining POST data.
Please structure your views so that redirects only occur during GETs."""
return HttpResponsePermanentRedirect(newurl)
|
More like this
- Template tag - list punctuation for a list of items by shapiromatron 9 months ago
- JSONRequestMiddleware adds a .json() method to your HttpRequests by cdcarter 9 months, 1 week ago
- Serializer factory with Django Rest Framework by julio 1 year, 4 months ago
- Image compression before saving the new model / work with JPG, PNG by Schleidens 1 year, 4 months ago
- Help text hyperlinks by sa2812 1 year, 5 months ago
Comments
Cool, I updated my code with your check for the webfaction header. I like the idea of checking both better than having two branches of the code.
Cheers
#
To accept both http and https requests with same view I added "secure!=1 and " to the line 20;
Then marked that view with {'SSL':1} in urls file.
#
Sorry, in my previous comment I forgotten the fact that 1==True in Python. So, the value "1" must be changed with some another value. I changed them with -1.
#
Nice work. I wanted to be able to run the middleware in dev (no ssl cert) and production (ssl cert) so, I made this change to lines 20&21:
I then added this line to my production settings.py and made it false in my dev settings.py:
SSL_ENABLED = True
#
I've found that I only want to explicitly choose a protocol in certain views, I have made a change to process_view that will only redirect when it is explicitly asked for ie:
Now any view with the SSL option included will be forced to the requested protocol but the others will be ignored.
#
Note!
If you are running under IIS with PyISAPIe the example PyISAPIe handler does not include an implementation for the is_secure method and you will end up with a redirect loop. This can be solved by modifying the PyISAPIeRequest class and adding the method:
This solution is not perfect as it is assumed that SSL is configured and working on port 443 but as mentioned in the function there is no other information that allows for a more "informed" decision.
#
Yes, like tim.savage, I find situations, where you should allow both http and https access, and leave them, as is.
That is a matter of taste, which is the default...
#
Lines 14 through 18 can be simplified into:
It's not much, but I think it creates a more readable section of code.
If you still want be able to develop with runserver, replace the secure assignment line with:
secure = view_kwargs.get(SSL, False) if not settings.DEBUG else False
and make sure you're in debug mode whenever you use runserver.
Great snippet, though. Thanks!
#
As spookeylukey wrote, what you really want is:
#
In Django 1.5 django.http.get_host() is replaced with request.get_host()
#
Please login first before commenting.