Rev 6164 breaks HTTPS redirects

14 views
Skip to first unread message

Jeffrey Froman

unread,
Sep 24, 2007, 1:54:40 PM9/24/07
to Django developers
Hello,

As of revision #6164, when accessing my site over HTTPS, all redirects
are being sent to plain HTTP urls.

It seems that this is because build_absolute_uri() uses
request.is_secure() to determine if the absolute url should be secure,
and request.is_secure() makes its determination based on whether or
not the variable 'HTTPS' is set to "on" in the operating system
environment. But this variable is not set in the operating system
environment in all cases. In particular, it does not work when
deploying Django through mod_wsgi. I have not tested HTTPS redirects
through fastcgi, but I suspect a similar problem may exist there. Can
anyone verify or debunk this suspicion?

I'm not sure what the right solution is to this problem, or if it is
best addressed in Django, or mod_wsgi, or somewhere else.


Thank you,
Jeffrey

--
Jeffrey Froman
OlympusNet Administration
http://www.olympus.net
wsgi.tc...@olympus.net

Graham Dumpleton

unread,
Sep 24, 2007, 6:58:58 PM9/24/07
to Django developers

On Sep 25, 3:54 am, Jeffrey Froman <wsgi.tc...@olympus.net> wrote:
> Hello,
>
> As of revision #6164, when accessing my site over HTTPS, all redirects
> are being sent to plain HTTP urls.
>
> It seems that this is because build_absolute_uri() uses
> request.is_secure() to determine if the absolute url should be secure,
> and request.is_secure() makes its determination based on whether or
> not the variable 'HTTPS' is set to "on" in the operating system
> environment. But this variable is not set in the operating system
> environment in all cases. In particular, it does not work when
> deploying Django through mod_wsgi. I have not tested HTTPS redirects
> through fastcgi, but I suspect a similar problem may exist there. Can
> anyone verify or debunk this suspicion?
>
> I'm not sure what the right solution is to this problem, or if it is
> best addressed in Django, or mod_wsgi, or somewhere else.

In WSGI indication of https is specified by:

wsgi.url_scheme A string representing the "scheme" portion of the URL
at which the application is being invoked. Normally, this will have
the value "http" or "https", as appropriate.

If HTTPS is being set then it is incidental and should not be relied
upon. If the internals of Django is expecting HTTPS to be set, in the
WSGI interface it should check for wsgi.url_scheme and if it is
'https' then set HTTPS explicitly to 'on'. Ie., translate from WSGI
convention.

Also, Django should possibly be checking for more than just 'on' as
value for HTTPS as some web servers pass '1' rather than 'on' anyway.
The WSGI PEP even shows an example for a CGI/WSGI adapater as such
where it caters for this.

if environ.get('HTTPS','off') in ('on','1'):
environ['wsgi.url_scheme'] = 'https'
else:
environ['wsgi.url_scheme'] = 'http'

The WSGI specification also show the correct way of constructing an
absolute URL:

from urllib import quote
url = environ['wsgi.url_scheme']+'://'

if environ.get('HTTP_HOST'):
url += environ['HTTP_HOST']
else:
url += environ['SERVER_NAME']

if environ['wsgi.url_scheme'] == 'https':
if environ['SERVER_PORT'] != '443':
url += ':' + environ['SERVER_PORT']
else:
if environ['SERVER_PORT'] != '80':
url += ':' + environ['SERVER_PORT']

url += quote(environ.get('SCRIPT_NAME',''))
url += quote(environ.get('PATH_INFO',''))
if environ.get('QUERY_STRING'):
url += '?' + environ['QUERY_STRING']

Note how it relies on wsgi.url_scheme and not HTTPS.

In summary, Django code needs to be updated so that its WSGI adapter
sets HTTPS explicitly based on wsgi.url_scheme if it wants to use
HTTPS internally.

Graham

Ramiro Morales

unread,
Sep 24, 2007, 7:59:16 PM9/24/07
to django-d...@googlegroups.com
On 9/24/07, Graham Dumpleton <Graham.D...@gmail.com> wrote:
>
> In WSGI indication of https is specified by:
>
> wsgi.url_scheme A string representing the "scheme" portion of the URL
> at which the application is being invoked. Normally, this will have
> the value "http" or "https", as appropriate.
>
> If HTTPS is being set then it is incidental and should not be relied
> upon. If the internals of Django is expecting HTTPS to be set...
> [...]
>

Fortunately this is being handled by an abstraction in the form of a is_secure()
method on the Django handler classes on django.core.handlers.
Perhaps a patch like this would solve Jeffrey problems?:

--- a/django/core/handlers/wsgi.py Mon Sep 24 15:27:42 2007 -0300
+++ b/django/core/handlers/wsgi.py Mon Sep 24 20:56:59 2007 -0300
@@ -105,7 +105,7 @@ class WSGIRequest(http.HttpRequest):
return '%s%s' % (self.path, self.environ.get('QUERY_STRING',
'') and ('?' + self.environ.get('QUERY_STRING', '')) or '')

def is_secure(self):
- return 'HTTPS' in self.environ and self.environ['HTTPS'] == 'on'
+ return 'wsgi.url_scheme' in self.environ and
self.environ['wsgi.url_scheme'] == 'https'

def _load_post_and_files(self):
# Populates self._post and self._files


Regards,

--
Ramiro Morales

Ramiro Morales

unread,
Sep 25, 2007, 10:12:23 AM9/25/07
to django-d...@googlegroups.com
On 9/24/07, Ramiro Morales <cra...@gmail.com> wrote:

>
> Fortunately this is being handled by an abstraction in the form of a is_secure()
> method on the Django handler classes on django.core.handlers.
> Perhaps a patch like this would solve Jeffrey problems?:
>
> --- a/django/core/handlers/wsgi.py Mon Sep 24 15:27:42 2007 -0300
> +++ b/django/core/handlers/wsgi.py Mon Sep 24 20:56:59 2007 -0300
> @@ -105,7 +105,7 @@ class WSGIRequest(http.HttpRequest):
> return '%s%s' % (self.path, self.environ.get('QUERY_STRING',
> '') and ('?' + self.environ.get('QUERY_STRING', '')) or '')
>
> def is_secure(self):
> - return 'HTTPS' in self.environ and self.environ['HTTPS'] == 'on'
> + return 'wsgi.url_scheme' in self.environ and
> self.environ['wsgi.url_scheme'] == 'https'
>
> def _load_post_and_files(self):
> # Populates self._post and self._files
>

Jeffrey, can you test that patch on your enviroment and report back
if it solves the problem so a ticket+patch can be created on Django Trac?.

Thanks,

PS: This is also being discussed on the mod_wsgi mailing list:

http://groups.google.com/group/modwsgi/browse_frm/thread/9cb69065a92e8f29?hl=en

--
Ramiro Morales

Jeffrey Froman

unread,
Sep 25, 2007, 1:48:00 PM9/25/07
to Django developers
On Sep 25, 7:12 am, "Ramiro Morales" <cra...@gmail.com> wrote:

> Jeffrey, can you test that patch on your enviroment and report back
> if it solves the problem so a ticket+patch can be created on Django Trac?.

I have tested this fix, and it appears to work perfectly here, passing
all redirection tests. I shortened the change a little to:

def is_secure(self):
return self.environ.get('wsgi.url_scheme') == 'https'

but the fix works both ways.

Thank you,
Jeffrey

Jeffrey Froman

unread,
Sep 26, 2007, 3:03:32 PM9/26/07
to Django developers
I see the new ticket in Trac:
http://code.djangoproject.com/ticket/5604

Thanks very much for creating the ticket, and submitting the patch.


Jeffrey

Reply all
Reply to author
Forward
0 new messages