Skip to content

http.client accepts Content-Length and chunk-size values that RFC 9112 forbids #150751

@metsw24-max

Description

@metsw24-max

http.client derives the response body framing from int() of the Content-Length header and the chunked chunk-size line:

  • HTTPResponse.begin: self.length = int(length)
  • HTTPResponse._read_next_chunk_size: return int(line, 16)

RFC 9112 defines Content-Length = 1*DIGIT and chunk-size = 1*HEXDIG, but int() is more permissive: it accepts a leading +/-, underscores, surrounding whitespace and, in base 16, an 0x prefix and non-ASCII digits. So values like Content-Length: +5 / 5_0 and chunk sizes -5, +5, 0x5, 1_f are accepted and used to frame the body, while an RFC-compliant front end would reject them or frame the message differently (CWE-444).

Reproducer:

import http.client, io
class S:
    def __init__(s, d): s.f = io.BytesIO(d)
    def makefile(s, *a, **k): return s.f
def parse(raw):
    r = http.client.HTTPResponse(S(raw)); r.begin(); return r
raw = b'HTTP/1.1 200 OK\r\nTransfer-Encoding: chunked\r\n\r\n+5\r\nHELLO\r\n0\r\n\r\n'
print(parse(raw).read())            # b'HELLO' -- '+5' is not a HEXDIG
raw = b'HTTP/1.1 200 OK\r\nContent-Length: 5_0\r\n\r\n' + b'A'*50
print(parse(raw).length)            # 50 -- '5_0' is not 1*DIGIT

The body-framing tokens should be validated against the grammar before being passed to int().

Linked PRs

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions