#27961: HTTP_X_FORWARDED_PROTO is bypassed
-------------------------------------+-------------------------------------
Reporter: cryptogun | Owner: nobody
Type: Bug | Status: new
Component: HTTP handling | Version: 1.10
Severity: Normal | Resolution:
Keywords: redirect HTTPS X | Triage Stage:
-Forwarded-Proto | Unreviewed
Has patch: 0 | Needs documentation: 0
Needs tests: 0 | Patch needs improvement: 0
Easy pickings: 0 | UI/UX: 0
-------------------------------------+-------------------------------------
Comment (by cryptogun):
Update. There is no security vulnerability here. The problem is: A
misconfigured setting `SECURE_PROXY_SSL_HEADER` is ignored.
Explanation:
{{{
@property
def scheme(self):
if settings.SECURE_PROXY_SSL_HEADER:
try:
header, value = settings.SECURE_PROXY_SSL_HEADER
except ValueError:
raise ImproperlyConfigured(
'The SECURE_PROXY_SSL_HEADER setting must be a tuple
containing two values.'
)
if self.META.get(header) == value:
return 'https'
return self._get_scheme()
}}}
If `self.META.get(header) != value`, outer `if` ended with no `return`, so
the last line `return self._get_scheme()` is executed. i.e.
- A misconfigured setting `SECURE_PROXY_SSL_HEADER =
('HTTP_X_FORWARDED_PROTO', 'httpssssssss')` is ignored without giving a
warning and then fallback to `self._get_scheme()`.
- `_get_scheme()` is overridden to `return
self.environ.get('wsgi.url_scheme')`. See
[https://github.com/django/django/blob/master/django/core/handlers/wsgi.py#L105
here].
- Gunicorn sets that environ. See
[https://github.com/benoitc/gunicorn/blob/master/gunicorn/http/wsgi.py#L140-L142
here].
- Gunicorn sets that environ by comparing request header to gunicorn
setting `secure_scheme_headers` (default is `{'X-FORWARDED-PROTOCOL':
'ssl', 'X-FORWARDED-PROTO': 'https', 'X-FORWARDED-SSL': 'on'}`). See
[http://docs.gunicorn.org/en/stable/settings.html#secure-scheme-headers
here].
As for my 5 case:
Case 2 and 3 are misconfigurating `SECURE_PROXY_SSL_HEADER` in django.
Scheme is determined by gunicorn without a warning.
Case 1, 4, 5: no problem, safe, works great.
2.
nginx sets header: `proto` (Abbr. of `X-Forwarded-Proto` header, you know
what I mean.)
django gets scheme by checking this header: `proto == httpssssssss` (Abbr.
of `SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_PROTO', 'httpssssssss')`)
gunicorn gets scheme by checking these header: `proto ssl protocol`
result: HTTP: redirect. Then Chrome re-request via HTTPS. HTTPS: accept.
conclusion: Safe. Fallback to gunicorn.
3.
nginx: `proto`
django: `protasdf == httpssssssss`
gunicorn: `proto ssl protocol`
result: HTTP: redirect. HTTPS: accept.
conclusion: Safe. Fallback to gunicorn.
1.
nginx: `proto`
django: `None`
gunicorn: `proto ssl protocol`
result: HTTP: redirect. HTTPS: accept.
conclusion: Safe. Django let gunicorn to determine the scheme.
4.
nginx: `protooo`
gunicorn: `proto ssl protocol`
django: `protooo == https`
result: HTTP: redirect. HTTPS: accept.
conclusion: Safe. Django determines the scheme.
5.
nginx: `protooo`
gunicorn: `proto ssl protocol`
django: `protooo == httpsssss`
result: HTTP: redirect. HTTPS: redirect.
conclusion: Safe, redirect anything. Django determines the scheme.
`httpsssss != https`, so HTTPS also redirected.
6.
nginx: `proto`
gunicorn: `proto ssl protocol`
django: `proto == httpssssssss`
result: HTTP: redirect. HTTPS: redirect.
conclusion: Safe. An else clause was added so the code won't fallback to
gunicorn. Django determines the scheme, in contrast with case 2.
`httpssssssss != https`, so HTTPS also redirected.
--
Ticket URL: <https://code.djangoproject.com/ticket/27961#comment:7>
Django <https://code.djangoproject.com/>
The Web framework for perfectionists with deadlines.
--
You received this message because you are subscribed to the Google Groups
"Django updates" group.
To unsubscribe from this group and stop receiving emails from it, send an email
to [email protected].
To post to this group, send email to [email protected].
To view this discussion on the web visit
https://groups.google.com/d/msgid/django-updates/067.8481b3eb90dd7a309e62fedf4174e3ed%40djangoproject.com.
For more options, visit https://groups.google.com/d/optout.