Repository: jupyterhub/jupyterhub

Issue:
CookieError Ilegal key %r, Jupyterhub not sanitizing header keys properly
<!-- Thank you for contributing. These HTML comments will not render in the issue, but you can delete them once you've read them if you prefer! -->

### Bug description

<!-- Use this section to clearly and concisely describe the bug. -->

End users occasionally are receiving timeouts on **Jupyterhub 3.1.0**. On review, the error log `/var/log/jupyterhub.log` contains many unexpected references to an Illegal Key in HTTPCookie.

#### Expected behaviour

<!-- Tell us what you thought would happen. -->

End users running R kernel ipynbs should not get unexpected timeouts due to invalid key in HTTP Cookies when authenticating to the hub via GitHub OAuth.

#### Actual behaviour

<!-- Tell us what actually happens. -->

End users occasionally get timeouts. Below is a reference of the actual error log, the first hit. Interestingly the error log has thousands of hits for that same illegal key value, which I believe is a Google Analytics tag & measurement ID.

```python
[E 2023-01-24 00:12:16.024 JupyterHub web:1798] Uncaught exception GET / (::ffff:xxx.xxx.xxx.xxx)
    HTTPServerRequest(protocol='https', host='commjhub.asc.upenn.edu', method='GET', uri='/', version='HTTP/1.1', remote_ip='::ffff:xxx.xxx.xxx.xxx')
    Traceback (most recent call last):
      File "/usr/local/anaconda3/lib/python3.8/site-packages/tornado/web.py", line 1711, in _execute
        result = method(*self.path_args, **self.path_kwargs)
      File "/usr/local/anaconda3/lib/python3.8/site-packages/jupyterhub/handlers/base.py", line 1391, in get
        self.redirect(url_path_join(self.hub.base_url, path), permanent=False)
      File "/usr/local/anaconda3/lib/python3.8/site-packages/tornado/web.py", line 816, in redirect
        self.finish()
      File "/usr/local/anaconda3/lib/python3.8/site-packages/jupyterhub/handlers/base.py", line 184, in finish
        super().finish(*args, **kwargs)
      File "/usr/local/anaconda3/lib/python3.8/site-packages/tornado/web.py", line 1161, in finish
        self._log()
      File "/usr/local/anaconda3/lib/python3.8/site-packages/tornado/web.py", line 1746, in _log
        self.application.log_request(self)
      File "/usr/local/anaconda3/lib/python3.8/site-packages/tornado/web.py", line 2262, in log_request
        self.settings["log_function"](handler)
      File "/usr/local/anaconda3/lib/python3.8/site-packages/jupyterhub/log.py", line 143, in log_request
        headers = _scrub_headers(request.headers)
      File "/usr/local/anaconda3/lib/python3.8/site-packages/jupyterhub/log.py", line 108, in _scrub_headers
        c = SimpleCookie(headers['Cookie'])
      File "/usr/local/anaconda3/lib/python3.8/http/cookies.py", line 480, in __init__
        self.load(input)
      File "/usr/local/anaconda3/lib/python3.8/http/cookies.py", line 529, in load
        self.__parse_string(rawdata)
      File "/usr/local/anaconda3/lib/python3.8/http/cookies.py", line 593, in __parse_string
        self.__set(key, rval, cval)
      File "/usr/local/anaconda3/lib/python3.8/http/cookies.py", line 485, in __set
        M.set(key, real_value, coded_value)
      File "/usr/local/anaconda3/lib/python3.8/http/cookies.py", line 352, in set
        raise CookieError('Illegal key %r' % (key,))
    http.cookies.CookieError: Illegal key '_ga_SR2P22QVQQ,G-0HYE8YG0M6'
```

### How to reproduce

<!-- Use this section to describe the steps that a user would take to experience this bug. -->

Pass an invalid key for HTTP cookies. In my case, I think it's the comma value in the GA `http.cookies.CookieError: Illegal key '_ga_SR2P22QVQQ,G-0HYE8YG0M6'` based on information here:  https://docs.python.org/3/library/http.cookies.html#module-http.cookies
> Note: On encountering an invalid cookie, CookieError is raised, so if your cookie data comes from a browser you should always prepare for invalid data and catch CookieError on parsing.

### Your personal set up

<!--
Tell us a little about the system you're using.
Please include information about how you installed,
e.g. are you using a distribution such as zero-to-jupyterhub or the-littlest-jupyterhub.
 -->

 - OS: Ubuntu 20.04.5 LTS
 <!-- [e.g. ubuntu 20.04, macOS 11.0] -->
 - Version(s): 
   - Jupyterhub 3.1.0
   - Python 3.8.8 
 <!-- e.g. jupyterhub --version, python --version --->




Discussion:
Thank you for opening your first issue in this project! Engagement like this is essential for open source projects! :hugs:
<br>If you haven't done so already, check out [Jupyter's Code of Conduct](https://github.com/jupyter/governance/blob/master/conduct/code_of_conduct.md). Also, please try to follow the issue template as it helps other other community members to contribute more effectively.
![welcome](https://raw.githubusercontent.com/jupyterhub/.github/master/images/welcome.jpg)
You can meet the other [Jovyans](https://jupyter.readthedocs.io/en/latest/community/content-community.html?highlight=jovyan#what-is-a-jovyan) by joining our [Discourse forum](http://discourse.jupyter.org/). There is also an intro thread there where you can stop by and say Hi! :wave:
<br>Welcome to the Jupyter community! :tada:

I believe the error is reproducible by manually adding a cookie to your browser session with an invalid char:

1. Right click **Inspect > Console** and add something with an invalid char from https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie#attributes
```console
document.cookie="_ga_EtienneIsACookieMonster,breakingnow=G-blaaaaahblahblah"
```
2. Confirm malformatted cookie is in **Inspect > Application > Cookies** 
3. Try and run a notebook with Python or R kernel
4. Observe the Kernel shows _Unknown_ and the cell does not run
5. Delete the malformatted cookie and rerun cell

![image](https://user-images.githubusercontent.com/44907911/218152480-a15cba23-e18c-49a3-8456-fd3e0979e449.png)

It might just be the case that Jupyterhub isn't sanitizing HTTPCookies which is unexpected and arguably a bug depending on your perspective.. In my case, I think end users were respectively accessing _some_ publicly accessible site that has a malformatted GA4 tag (the migration from UA to GA4 is ongoing for 2023, so it makes sense this just came up randomly)
Searching back I did see some related issues... there was a commit here that I thought is maybe relevant: https://github.com/ipython/ipython/pull/4762/commits/4c5aa28b94c5f8ad4872d1230ce4b6d158de6722

That was to sanitize cookies for username.. in this case I'm suggesting sanitize other cookies in browser 

Resolving patch:
diff --git a/jupyterhub/log.py b/jupyterhub/log.py
--- a/jupyterhub/log.py
+++ b/jupyterhub/log.py
@@ -105,11 +105,16 @@ def _scrub_headers(headers):
             auth_type = ''
         headers['Authorization'] = f'{auth_type} [secret]'
     if 'Cookie' in headers:
-        c = SimpleCookie(headers['Cookie'])
-        redacted = []
-        for name in c.keys():
-            redacted.append(f"{name}=[secret]")
-        headers['Cookie'] = '; '.join(redacted)
+        try:
+            c = SimpleCookie(headers['Cookie'])
+        except Exception as e:
+            # it's possible for browsers to send invalid cookies
+            headers['Cookie'] = f"Invalid Cookie: {e}"
+        else:
+            redacted = []
+            for name in c.keys():
+                redacted.append(f"{name}=[secret]")
+            headers['Cookie'] = '; '.join(redacted)
     return headers
 
 


Test patch: