Repository: benoitc/gunicorn

Issue:
Authorization header parsing can't handle unconventional values
The [_get_user](https://github.com/benoitc/gunicorn/blob/c8f820b95a559aec81167c854d7b0d869316ecb3/gunicorn/glogging.py#L381) method assumes that Basic authorization method is always used in the Authorization header. This is not true, especially for APIs which can have their own token format (not necessarily b64 encoded).

For instance `Authorization: Bearer whatever` header results in (Python 3.5; current master):

```
Traceback (most recent call last):
  File "/usr/src/deps/gunicorn/gunicorn/workers/async.py", line 116, in handle_request
    self.log.access(resp, req, environ, request_time)
  File "/usr/src/deps/gunicorn/gunicorn/glogging.py", line 287, in access
    request_time))
  File "/usr/src/deps/gunicorn/gunicorn/glogging.py", line 243, in atoms
    'u': self._get_user(environ) or '-',
  File "/usr/src/deps/gunicorn/gunicorn/glogging.py", line 392, in _get_user
    auth = auth.decode('utf-8')
UnicodeDecodeError: 'utf-8' codec can't decode byte 0xc2 in position 0: invalid continuation byte
```

Depending on the input header value, the b64 operation itself can fail as well:

```
Traceback (most recent call last):
  File "/usr/src/deps/gunicorn/gunicorn/workers/async.py", line 116, in handle_request
    self.log.access(resp, req, environ, request_time)
  File "/usr/src/deps/gunicorn/gunicorn/glogging.py", line 287, in access
    request_time))
  File "/usr/src/deps/gunicorn/gunicorn/glogging.py", line 243, in atoms
    'u': self._get_user(environ) or '-',
  File "/usr/src/deps/gunicorn/gunicorn/glogging.py", line 390, in _get_user
    auth = base64.b64decode(auth[1].strip().encode('utf-8'))
  File "/usr/local/lib/python3.5/base64.py", line 90, in b64decode
    return binascii.a2b_base64(s)
binascii.Error: Incorrect padding
```

I suggest checking that the auth type is `Basic` before trying to do base64 operations on the 2nd part of the header. Handling those possible exceptions would also be nice, since someone might not know that `Basic` headers need to be base64 encoded.



Discussion:
:+1: we should fix this
- Should never cause a crash
- Should be quiet except maybe to warn once if the format seems like bad use of basic auth

FYI the change to the logging have been introduced in 5f3dc8ed2bb5761846f20cfd15d1d44e711fbed1 . Any quick fix in mind?


Resolving patch:
diff --git a/gunicorn/glogging.py b/gunicorn/glogging.py
--- a/gunicorn/glogging.py
+++ b/gunicorn/glogging.py
@@ -4,6 +4,7 @@
 # See the NOTICE for more information.
 
 import base64
+import binascii
 import time
 import logging
 logging.Logger.manager.emittedNoHandlerWarning = 1
@@ -381,7 +382,7 @@ def _set_syslog_handler(self, log, cfg, fmt, name):
     def _get_user(self, environ):
         user = None
         http_auth = environ.get("HTTP_AUTHORIZATION")
-        if http_auth:
+        if http_auth and http_auth.startswith('Basic'):
             auth = http_auth.split(" ", 1)
             if len(auth) == 2:
                 try:
@@ -394,6 +395,9 @@ def _get_user(self, environ):
                 except TypeError as exc:
                     self.debug("Couldn't get username: %s", exc)
                     return user
+                except binascii.Error as exc:
+                    self.debug("Couldn't get username: %s", exc)
+                    return user
                 if len(auth) == 2:
                     user = auth[0]
         return user


Test patch: