#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 django-updates+unsubscr...@googlegroups.com.
To post to this group, send email to django-updates@googlegroups.com.
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.

Reply via email to