Django HTTPS redirects broken under modwsgi

8 views
Skip to first unread message

Jeffrey Froman

unread,
Sep 24, 2007, 2:33:44 PM9/24/07
to modwsgi
Hello,

I recently started this thread on the Django developer's list:
http://groups.google.com/group/django-developers/browse_thread/thread/5af492b48fee4ff0

It seems that django now relies on:

os.environ['HTTPS'] == "on"

in order for HTTPS redirects to work. Previously, redirects were
returned as relative URLs, contrary to the HTTP spec, but satisfactory
for most web browsers. Now that redirects follow the spec in this
regard, they need some way to determine whether a request is secure or
not, and this is the method Django has chosen.

As far as I can tell, this variable is not set when running under
mod_wsgi, and hence HTTPS redirects are broken. I am bringing
attention to the thread here as well, since I really don't know what
the proper solution is.


Thank you,
Jeffrey

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

Graham Dumpleton

unread,
Sep 24, 2007, 7:00:55 PM9/24/07
to mod...@googlegroups.com
WSGI doesn't use 'HTTPS' as indicator of a secure web site, it uses
wsgi.url_scheme. See my followup to Django list post, but in short
Django needs to translate the WSGI convention into its own internal
convention in its WSGI adapter. If developers don't followup on post,
then create a ticket in Django issue system.

Graham

Nimrod A. Abing

unread,
Sep 25, 2007, 1:25:36 AM9/25/07
to mod...@googlegroups.com
On 9/25/07, Jeffrey Froman <wsgi....@olympus.net> wrote:
>
> Hello,
>
> I recently started this thread on the Django developer's list:
> http://groups.google.com/group/django-developers/browse_thread/thread/5af492b48fee4ff0
>
> It seems that django now relies on:
>
> os.environ['HTTPS'] == "on"
>
> in order for HTTPS redirects to work. Previously, redirects were
> returned as relative URLs, contrary to the HTTP spec, but satisfactory
> for most web browsers. Now that redirects follow the spec in this
> regard, they need some way to determine whether a request is secure or
> not, and this is the method Django has chosen.
>
> As far as I can tell, this variable is not set when running under
> mod_wsgi, and hence HTTPS redirects are broken. I am bringing
> attention to the thread here as well, since I really don't know what
> the proper solution is.

The fix is a modification to a single line in django/core/handlers/wsgi.py

Look for the function is_secure() and change the line that says:

return 'HTTPS' in self.environ and self.environ['HTTPS'] == 'on'

to

return 'wsgi.url_scheme' in self.environ and 'https' ==
self.environ['wsgi.url_scheme']

I don't know about the rest of Django but by running grep through the
latest stable version (0.96) there are other places where the HTTPS
environment variable is used to determine if the Django app is
accessed through a secure connection. The CGI 1.1 spec does not
require HTTPS environment variable to be set and some webservers do
not set it so it is not a reliable indicator if you are connected to
the server via HTTPS.

SERVER_PORT is part of the CGI 1.1 spec but you should not rely on
that one either since it's possible that the server is running HTTPS
on a different port from the standard 443 (e.g.,
proxied/load-balanced/chrooted servers come to mind).
--
_nimrod_a_abing_

http://abing.gotdns.com/
http://www.preownedcar.com/

Graham Dumpleton

unread,
Sep 25, 2007, 1:42:09 AM9/25/07
to mod...@googlegroups.com

FWIW, in mod_wsgi the wsgi.url_scheme should be reliable (Apache >=
2.0) as it derives the information from Apache mod_ssl module
directly. It actually sets HTTPS as a side effect as well, but it is
set to "1" and not "on" which is what Django was expecting.

Anyway, the mod_wsgi code first does:

/* Determine whether connection uses HTTPS protocol. */

#if AP_SERVER_MAJORVERSION_NUMBER >= 2
if (!wsgi_is_https)
wsgi_is_https = APR_RETRIEVE_OPTIONAL_FN(ssl_is_https);

if (wsgi_is_https && wsgi_is_https(r->connection))
apr_table_set(r->subprocess_env, "HTTPS", "1");
#endif

I used "1" rather than "on" as the WSGI specification suggests that it
is a value that existing web servers can produce. Ie., since I didn't
need to be setting it anyway, seemed a reasonable value to choose of
the two the WSGI specification gives as an example.

Later on it then constructs wsgi.url_scheme:

scheme = apr_table_get(r->subprocess_env, "HTTPS");

if (scheme && (!strcasecmp(scheme, "On") || !strcmp(scheme, "1"))) {
object = PyString_FromString("https");
PyDict_SetItemString(vars, "wsgi.url_scheme", object);
Py_DECREF(object);
}
else {
object = PyString_FromString("http");
PyDict_SetItemString(vars, "wsgi.url_scheme", object);
Py_DECREF(object);
}

Now I could use "on" instead of "1", but technically that would just
hide the fact that Django is not correct in that it by rights should
use 'wsgi.url_scheme' for a WSGI adapter instead.

BTW, if you don't want to go fiddling Django source code, use a
wrapper around Django entry point:

import os, sys
sys.path.append('/usr/local/django')
os.environ['DJANGO_SETTINGS_MODULE'] = 'mysite.settings'

import django.core.handlers.wsgi

_application = django.core.handlers.wsgi.WSGIHandler()

def application(environ, start_response):
if environ['wsgi.url_scheme'] == 'https':
environ['HTTPS'] = 'on'
return _application(environ, start_response)

Graham

Ramiro Morales

unread,
Sep 25, 2007, 10:12:12 AM9/25/07
to mod...@googlegroups.com

Yes there are other places in Django where this is handled right (or better)::

modpython.py:49
return self._req.subprocess_env.get('HTTPS', '').lower() in ('on', '1')

django\core\servers\basehttp.py:212 (server side POV)
if environ.get("HTTPS") in ('yes','on','1'):

> [...]


> Now I could use "on" instead of "1", but technically that would just
> hide the fact that Django is not correct in that it by rights should
> use 'wsgi.url_scheme' for a WSGI adapter instead.
>

Since the WSGI spec wording used is "should" for the 'HTTPS'
var and "must" for the 'wsgi.url_scheme' var when talking about
server side implementations of WSGI, IMVHO it would be safe to
just handle the latter case as in the the snippet posted bu Nimrod
and the one-line-patch I posted to the django-devel mailing list.

Regards,

--
Ramiro Morales

Jeffrey Froman

unread,
Sep 25, 2007, 1:15:37 PM9/25/07
to modwsgi
Thanks everyone for your thoughts and for the workarounds. Since there
seems to be general agreement that this should be fixed in Django, I
will follow up further in that thread. That thread's url seems to have
gotten cut off in my original post, here's a shorter one to the same
thread:

http://tinyurl.com/3cja2g


Thank you,
Jeffrey

Reply all
Reply to author
Forward
0 new messages