Skip to content

Commit

Permalink
drop header keys with underscores (#959)
Browse files Browse the repository at this point in the history
* drop header keys with underscores

* use a dedicated formalize_key_naming function for header normalisation

* adjust tests to comply with the new header security checks
  • Loading branch information
kevin-mizu committed May 6, 2024
1 parent 64d8516 commit ed743d7
Show file tree
Hide file tree
Showing 2 changed files with 23 additions and 3 deletions.
22 changes: 21 additions & 1 deletion eventlet/wsgi.py
Original file line number Diff line number Diff line change
Expand Up @@ -706,6 +706,23 @@ def get_client_address(self):
host = forward + ',' + host
return (host, port)

def formalize_key_naming(self, k):
"""
Headers containing underscores are permitted by RFC9110,
but evenlet joining headers of different names into
the same environment variable will dangerously confuse applications as to which is which.
Cf.
- Nginx: http://nginx.org/en/docs/http/ngx_http_core_module.html#underscores_in_headers
- Django: https://www.djangoproject.com/weblog/2015/jan/13/security/
- Gunicorn: https://github.com/benoitc/gunicorn/commit/72b8970dbf2bf3444eb2e8b12aeff1a3d5922a9a
- Werkzeug: https://github.com/pallets/werkzeug/commit/5ee439a692dc4474e0311de2496b567eed2d02cf
- ...
"""
if "_" in k:
return

return k.replace('-', '_').upper()

def get_environ(self):
env = self.server.get_environ()
env['REQUEST_METHOD'] = self.command
Expand Down Expand Up @@ -748,7 +765,10 @@ def get_environ(self):

env['headers_raw'] = headers_raw = tuple((k, v.strip(' \t\n\r')) for k, v in headers)
for k, v in headers_raw:
k = k.replace('-', '_').upper()
k = self.formalize_key_naming(k)
if not k:
continue

if k in ('CONTENT_TYPE', 'CONTENT_LENGTH'):
# These do not get the HTTP_ prefix and were handled above
continue
Expand Down
4 changes: 2 additions & 2 deletions tests/wsgi_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -1924,8 +1924,8 @@ def app(environ, start_response):
result = read_http(sock)
sock.close()
assert result.status == 'HTTP/1.1 200 OK', 'Received status {!r}'.format(result.status)
assert result.body == (b'HTTP_HOST: localhost\nHTTP_HTTP_X_ANY_K: two\n'
b'HTTP_PATH_INFO: foo\nHTTP_X_ANY_K: one\n')
assert result.body == (b'HTTP_HOST: localhost\n'
b'HTTP_PATH_INFO: foo\n')

def test_env_header_stripping(self):
def app(environ, start_response):
Expand Down

0 comments on commit ed743d7

Please sign in to comment.