Reject chunk extensions with bare CR bytes (RFC 9112)

Both WSGI and ASGI parsers now validate that chunk extensions
do not contain bare CR characters, which are not allowed per
RFC 9112.

Fixes #3556
This commit is contained in:
Benoit Chesneau 2026-03-26 15:08:54 +01:00
parent 7057fc9f89
commit bdb2ebd5a4
5 changed files with 24 additions and 1 deletions

View File

@ -15,6 +15,9 @@
- **HTTP/2 Documentation**: Fix `http_protocols` examples to use comma-separated string
instead of list syntax ([#3561](https://github.com/benoitc/gunicorn/issues/3561))
- **Chunked Encoding**: Reject chunk extensions containing bare CR bytes per RFC 9112
([#3556](https://github.com/benoitc/gunicorn/discussions/3556))
### Security
- **ASGI Parser Header Validation**: Add security checks per RFC 9110/9112:

View File

@ -15,6 +15,9 @@
- **HTTP/2 Documentation**: Fix `http_protocols` examples to use comma-separated string
instead of list syntax ([#3561](https://github.com/benoitc/gunicorn/issues/3561))
- **Chunked Encoding**: Reject chunk extensions containing bare CR bytes per RFC 9112
([#3556](https://github.com/benoitc/gunicorn/discussions/3556))
### Security
- **ASGI Parser Header Validation**: Add security checks per RFC 9110/9112:

View File

@ -670,6 +670,10 @@ class PythonProtocol:
# Handle chunk extensions (e.g., "5;ext=value")
semicolon = size_line.find(b';')
if semicolon != -1:
# RFC 9112: chunk-ext must not contain bare CR
chunk_ext = size_line[semicolon + 1:]
if b'\r' in chunk_ext:
raise ParseError("Invalid chunk extension: bare CR not allowed")
size_line = size_line[:semicolon]
# Strict validation: reject leading/trailing whitespace

View File

@ -6,7 +6,7 @@ import io
import sys
from gunicorn.http.errors import (NoMoreData, ChunkMissingTerminator,
InvalidChunkSize)
InvalidChunkSize, InvalidChunkExtension)
class ChunkedReader:
@ -90,6 +90,9 @@ class ChunkedReader:
# RFC9112 7.1.1: BWS before chunk-ext - but ONLY then
chunk_size, *chunk_ext = line.split(b";", 1)
if chunk_ext:
# RFC 9112: chunk-ext must not contain bare CR
if b'\r' in chunk_ext[0]:
raise InvalidChunkExtension("bare CR not allowed")
chunk_size = chunk_size.rstrip(b" \t")
if any(n not in b"0123456789abcdefABCDEF" for n in chunk_size):
raise InvalidChunkSize(chunk_size)

View File

@ -113,6 +113,16 @@ class ChunkMissingTerminator(IOError):
return "Invalid chunk terminator is not '\\r\\n': %r" % self.term
class InvalidChunkExtension(IOError):
"""Invalid chunk extension per RFC 9112."""
def __init__(self, reason):
self.reason = reason
def __str__(self):
return "Invalid chunk extension: %s" % self.reason
class LimitRequestLine(ParseException):
def __init__(self, size, max_size=None):
self.size = size