mirror of
https://github.com/frappe/gunicorn.git
synced 2026-01-14 11:09:11 +08:00
forbid lone CR/LF and NUL in headers
New parser rule: refuse HTTP requests where a header field value contains characters that a) should never appear there in the first place, b) might have lead to incorrect treatment in a proxy in front, and c) might lead to unintended behaviour in applications. From RFC 9110 section 5.5: "Field values containing CR, LF, or NUL characters are invalid and dangerous, due to the varying ways that implementations might parse and interpret those characters; a recipient of CR, LF, or NUL within a field value MUST either reject the message or replace each of those characters with SP before further processing or forwarding of that message."
This commit is contained in:
parent
e3fa50d1c5
commit
eda9d456d3
@ -2254,7 +2254,7 @@ class StripHeaderSpaces(Setting):
|
||||
This is known to induce vulnerabilities and is not compliant with the HTTP/1.1 standard.
|
||||
See https://portswigger.net/research/http-desync-attacks-request-smuggling-reborn.
|
||||
|
||||
Use with care and only if necessary. May be removed in a future version.
|
||||
Use with care and only if necessary. Deprecated; scheduled for removal in 25.0.0
|
||||
|
||||
.. versionadded:: 20.0.1
|
||||
"""
|
||||
@ -2274,9 +2274,13 @@ class PermitUnconventionalHTTPMethod(Setting):
|
||||
methods with lowercase characters or methods containing the # character.
|
||||
HTTP methods are case sensitive by definition, and merely uppercase by convention.
|
||||
|
||||
This option is provided to diagnose backwards-incompatible changes.
|
||||
If unset, Gunicorn will apply nonstandard restrictions and cause 400 response status
|
||||
in cases where otherwise 501 status is expected. While this option does modify that
|
||||
behaviour, it should not be depended upon to guarantee standards-compliant behaviour.
|
||||
Rather, it is provided temporarily, to assist in diagnosing backwards-incompatible
|
||||
changes around the incomplete application of those restrictions.
|
||||
|
||||
Use with care and only if necessary. May be removed in a future version.
|
||||
Use with care and only if necessary. Temporary; scheduled for removal in 24.0.0
|
||||
|
||||
.. versionadded:: 22.0.0
|
||||
"""
|
||||
@ -2296,7 +2300,8 @@ class PermitUnconventionalHTTPVersion(Setting):
|
||||
It is unusual to specify HTTP 1 versions other than 1.0 and 1.1.
|
||||
|
||||
This option is provided to diagnose backwards-incompatible changes.
|
||||
Use with care and only if necessary. May be removed in a future version.
|
||||
Use with care and only if necessary. Temporary; the precise effect of this option may
|
||||
change in a future version, or it may be removed altogether.
|
||||
|
||||
.. versionadded:: 22.0.0
|
||||
"""
|
||||
@ -2316,7 +2321,7 @@ class CasefoldHTTPMethod(Setting):
|
||||
|
||||
This option is provided because previous versions of gunicorn defaulted to this behaviour.
|
||||
|
||||
Use with care and only if necessary. May be removed in a future version.
|
||||
Use with care and only if necessary. Deprecated; scheduled for removal in 24.0.0
|
||||
|
||||
.. versionadded:: 22.0.0
|
||||
"""
|
||||
@ -2378,7 +2383,16 @@ class TolerateDangerousFraming(Setting):
|
||||
|
||||
This is known to induce vulnerabilities, but not strictly forbidden by RFC9112.
|
||||
|
||||
Use with care and only if necessary. May be removed in a future version.
|
||||
In any case, the connection is closed after the malformed request,
|
||||
as it is unclear if and at which boundary additional requests start.
|
||||
|
||||
Use with care and only if necessary.
|
||||
Temporary; will be changed or removed in a future version.
|
||||
|
||||
.. versionadded:: 22.0.0
|
||||
.. versionchanged: 22.1.0
|
||||
The newly added rejection of invalid and dangerous characters CR, LF and NUL in
|
||||
header field values is also controlled with this setting. rfc9110 permits both
|
||||
rejecting and SP-replacing. With this option set, Gunicorn passes the field value
|
||||
unchanged. With this option unset, Gunicorn rejects the request.
|
||||
"""
|
||||
|
||||
@ -28,6 +28,7 @@ TOKEN_RE = re.compile(r"[%s0-9a-zA-Z]+" % (re.escape(RFC9110_5_6_2_TOKEN_SPECIAL
|
||||
METHOD_BADCHAR_RE = re.compile("[a-z#]")
|
||||
# usually 1.0 or 1.1 - RFC9112 permits restricting to single-digit versions
|
||||
VERSION_RE = re.compile(r"HTTP/(\d)\.(\d)")
|
||||
RFC9110_5_5_INVALID_AND_DANGEROUS = re.compile(r"[\0\r\n]")
|
||||
|
||||
|
||||
class Message(object):
|
||||
@ -121,6 +122,12 @@ class Message(object):
|
||||
value.append(curr.strip("\t "))
|
||||
value = " ".join(value)
|
||||
|
||||
if RFC9110_5_5_INVALID_AND_DANGEROUS.search(value):
|
||||
if not self.cfg.tolerate_dangerous_framing:
|
||||
raise InvalidHeader(name)
|
||||
# value = RFC9110_5_5_INVALID_AND_DANGEROUS.sub(" ", value)
|
||||
self.force_close()
|
||||
|
||||
if header_length > self.limit_request_field_size > 0:
|
||||
raise LimitRequestHeaders("limit request headers fields size")
|
||||
|
||||
|
||||
7
tests/requests/invalid/invalid_field_value_01.http
Normal file
7
tests/requests/invalid/invalid_field_value_01.http
Normal file
@ -0,0 +1,7 @@
|
||||
GET / HTTP/1.1\r\n
|
||||
Host: x\r\n
|
||||
Newline: a\n
|
||||
Content-Length: 26\r\n
|
||||
GET / HTTP/1.1\n
|
||||
Host: x\r\n
|
||||
\r\n
|
||||
5
tests/requests/invalid/invalid_field_value_01.py
Normal file
5
tests/requests/invalid/invalid_field_value_01.py
Normal file
@ -0,0 +1,5 @@
|
||||
from gunicorn.config import Config
|
||||
from gunicorn.http.errors import InvalidHeader
|
||||
|
||||
cfg = Config()
|
||||
request = InvalidHeader
|
||||
8
tests/requests/valid/invalid_field_value_01_compat.http
Normal file
8
tests/requests/valid/invalid_field_value_01_compat.http
Normal file
@ -0,0 +1,8 @@
|
||||
GET / HTTP/1.1\r\n
|
||||
Host: x\r\n
|
||||
Newline: a\n
|
||||
Content-Length: 26\r\n
|
||||
X-Forwarded-By: broken-proxy\r\n\r\n
|
||||
GET / HTTP/1.1\n
|
||||
Host: x\r\n
|
||||
\r\n
|
||||
18
tests/requests/valid/invalid_field_value_01_compat.py
Normal file
18
tests/requests/valid/invalid_field_value_01_compat.py
Normal file
@ -0,0 +1,18 @@
|
||||
from gunicorn.config import Config
|
||||
|
||||
cfg = Config()
|
||||
cfg.set("tolerate_dangerous_framing", True)
|
||||
|
||||
req1 = {
|
||||
"method": "GET",
|
||||
"uri": uri("/"),
|
||||
"version": (1, 1),
|
||||
"headers": [
|
||||
("HOST", "x"),
|
||||
("NEWLINE", "a\nContent-Length: 26"),
|
||||
("X-FORWARDED-BY", "broken-proxy"),
|
||||
],
|
||||
"body": b""
|
||||
}
|
||||
|
||||
request = [req1]
|
||||
Loading…
x
Reference in New Issue
Block a user