Internet-Draft | Using HTTP/3 Stream Limits in HTTP/2 | November 2023 |
Thomson & Pardue | Expires 10 May 2024 | [Page] |
A variant mechanism for managing stream limits is described for HTTP/2. This scheme is based on that used in QUIC and is more robust against certain patterns of abuse.¶
This note is to be removed before publishing as an RFC.¶
The latest revision of this draft can be found at https://martinthomson.github.io/h2-stream-limits/draft-thomson-httpbis-h2-stream-limits.html. Status information for this document may be found at https://datatracker.ietf.org/doc/draft-thomson-httpbis-h2-stream-limits/.¶
Discussion of this document takes place on the HTTP Working Group mailing list (mailto:ietf-http-wg@w3.org), which is archived at https://lists.w3.org/Archives/Public/ietf-http-wg/.¶
Source for this draft and an issue tracker can be found at https://github.com/martinthomson/h2-stream-limits.¶
This Internet-Draft is submitted in full conformance with the provisions of BCP 78 and BCP 79.¶
Internet-Drafts are working documents of the Internet Engineering Task Force (IETF). Note that other groups may also distribute working documents as Internet-Drafts. The list of current Internet-Drafts is at https://datatracker.ietf.org/drafts/current/.¶
Internet-Drafts are draft documents valid for a maximum of six months and may be updated, replaced, or obsoleted by other documents at any time. It is inappropriate to use Internet-Drafts as reference material or to cite them other than as "work in progress."¶
This Internet-Draft will expire on 10 May 2024.¶
Copyright (c) 2023 IETF Trust and the persons identified as the document authors. All rights reserved.¶
This document is subject to BCP 78 and the IETF Trust's Legal Provisions Relating to IETF Documents (https://trustee.ietf.org/license-info) in effect on the date of publication of this document. Please review these documents carefully, as they describe your rights and restrictions with respect to this document. Code Components extracted from this document must include Revised BSD License text as described in Section 4.e of the Trust Legal Provisions and are provided without warranty as described in the Revised BSD License.¶
HTTP/2 allows endpoints to declare the concurrency limit for streams created by
their peer (SETTINGS_MAX_CONCURRENT_STREAMS
; see Section 5.1.2 of [HTTP/2]). Initially, the stream concurrency limit is only bounded by the
maximum number of streams that can be created, which is 230 for each
endpoint, but the value can be changed at any time. Most endpoints set a smaller
value in an attempt to protect the resources that are committed when processing
a stream.¶
This limit is not effective in the case that streams are quickly cancelled.
The creator of a stream can cancel it using the RST_STREAM
frame. The
creator of a stream could also cause its peer to send RST_STREAM
by
purposefully sending frames that violate HTTP/2 rules, unless errors also
cause the peer to close the connection. Either of these methods has
deterministic and immediate effect, which means that the stream no longer counts
against the concurrent stream limit. This means that a malicious endpoint can
create or cancel an unbounded number of streams as long as its peer does not set
a limit of zero. For clarity, we'll refer to either of these methods as
"cancelling".¶
If the creation of the stream results in the expenditure of resources, rapidly
creating and cancelling streams can exhaust resources. For a server, clients
that create and cancel many requests can effectively deny service; for a client,
reserving and cancelling promised streams might have similar effect if a client
does not disable server push using the SETTINGS_ENABLE_PUSH
setting. This
creates a denial of service exposure for which a remedy is not supported in the
protocol.¶
However, as noted in Section 10.5 of [HTTP/2], many of the features in HTTP/2 that have some potential to create denial of service attacks also have constructive uses. Distinguishing constructive and destructive uses is often challenging. Some applications of HTTP might find that cancelling many requests is necessary; if cancellation is treated as abusive, it is possible that the treatment necessary to prevent attacks might unintentionally punish some clients.¶
The QUIC protocol [QUIC] on which HTTP/3 [HTTP/3] is built contains an alternative mechanism for limiting concurrency. The scheme in QUIC, as described in Section 4.6 of [QUIC], does not set a concurrent stream limit, but instead relies on a peer increasing the maximum allowed stream identifier. An endpoint cannot create new streams immediately after cancelling an open stream; their peer needs to send a message to make more streams available. In QUIC therefore, a malicious endpoint can only create and cancel a finite number of streams, after which their peer needs to provide consent to continue.¶
This document ports the QUIC stream concurrency limit mechanisms to HTTP/2. As use of this system is voluntary and therefore not necessarily guaranteed, this does not prevent abusive use of stream cancellation. However, deployment of this mechanism in popular implementations might allow endpoints to deploy more aggressive strategies for managing abuse in its absence.¶
The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in BCP 14 [RFC2119] [RFC8174] when, and only when, they appear in all capitals, as shown here.¶
The MAX_STREAMS
frame (type=0xTBD) is used to limit the streams that a
recipient is permitted to create.¶
The format of the MAX_STREAMS
frame is illustrated in Figure 1,
using the notation defined in Section 1.3 of [QUIC].¶
In addition to the common HTTP/2 frame header (see Section 4 of [HTTP/2]),
the MAX_STREAMS
contains a one-bit reserved field and a 31-bit Maxmimum Stream
Identifier field.¶
The Reserved field MUST be set to zero when sending and ignored on receipt.¶
The Maximum Stream Identifier field contains the maximum value of stream identifier that a recipient of this frame can use when it creates or reserves a stream; see Section 5.1 of [HTTP/2] for how streams are created.¶
A MAX_STREAMS
frame MUST be sent on stream 0. Receipt of a MAX_STREAMS
frame on any other stream MUST be treated as a connection error of type
PROTOCOL_ERROR; see Section 5.4.1 of [HTTP/2].¶
An endpoint MUST treat receipt of a MAX_STREAMS
frame with a length other than
4 as a connection error of type FRAME_SIZE_ERROR; see Section 5.4.1 of [HTTP/2].¶
A client MUST treat receipt of an even-numbered Maximum Stream Identifier as a connection error of type PROTOCOL_ERROR; see Section 5.4.1 of [HTTP/2], with an exception of a value of 0, which can be used to indicate support for the feature without enabling the creation of streams (see Section 3.2). Similarly, a server MUST treat receipt of an odd-numbered Maximum Stream Identifier as a connection error.¶
An endpoint that receives a MAX_STREAMS
frame MUST NOT create or reserve a
stream with a number that exceeds the value of the Maximum Stream Identifier
field from that frame, until it receives a MAX_STREAMS
frame with a larger
value.¶
An endpoint MUST treat the creation or reservation of a stream with a higher
valued stream identifier than it included in a MAX_STREAMS
frame as a
connection error of type FLOW_CONTROL_ERROR; see Section 5.4.1 of [HTTP/2].¶
Endpoints can only increase the value that they include in a MAX_STREAMS
frame. An endpoint MUST treat receipt of a Maximum Stream Identifier that is
equal to or smaller than a value that it has previously received as a connection
error of type PROTOCOL_ERROR; see Section 5.4.1 of [HTTP/2]. Note that a
value of 0 can be sent one time by either client or server to indicate support
for this feature without permitting the creation of streams; see Section 3.2.¶
An implementation can support a similar concurrency limit to that provided by
SETTINGS_MAX_CONCURRENT_STREAMS
. The initial value can be set to twice the
value of SETTINGS_MAX_CONCURRENT_STREAMS
, with a client adding 1 if the value
is non-zero. The endpoint then increases the value of the Maximum Stream
Identifier field as the streams its peer initiates are closed. An endpoint can
avoid sending redundant MAX_STREAMS
frames by processing all incoming data
before increasing the maximum. This approach differs from
SETTINGS_MAX_CONCURRENT_STREAMS
in that a peer is not able to create and
cancel streams arbitrarily, as new streams do not become available until after
receiving a MAX_STREAMS
frame.¶
This extension is not negotiated using the SETTINGS
frame. Instead, the
receipt of a MAX_STREAMS
frame indicates support for the feature.¶
An endpoint that supports this extension MUST send a MAX_STREAMS
frame after
establishing a connection, after the HTTP/2 connection preface and SETTINGS
frame (Section 3.4 of [HTTP/2]). When a MAX_STREAMS
frame is received, the
endpoint MUST subsequently only create streams according to the rules in
Section 3.1 and ignore any value of the SETTINGS_MAX_CONCURRENT_STREAMS
setting.¶
HTTP/2 considered the cancellation of a stream to reclaim the resources that might have been committed during its creation. The stream limits in HTTP/2 therefore did not provide a means to limit stream creation. Though cancellation is a potential source of abusive traffic, it was not explicitly mentioned (see Section 10.5 of [HTTP/2]). However, experience has shown that the creation of a stream has costs that cannot always be recovered when that stream is cancelled.¶
The use of the MAX_STREAMS
frame provides endpoints with a means of limiting
stream creation, using a method that has proven to be effective in QUIC.
However, repeated use of the MAX_STREAMS
frame is also a potential source of
abuse, as described in Section 10.5 of [HTTP/2]. Though processing a
MAX_STREAMS
frame is likely to be trivial, receiving significantly more
MAX_STREAMS
frames than the number of streams that have been closed might
indicate an attempt to waste effort.¶
This document registers a new entry in the "HTTP/2 Frame Type" registry, as documented in Section 11 of [HTTP/2]. This entry has the following values.¶
This document is written in response to the problems discovered as part of [CVE-2023-44487], so maybe those behind the botnet responsible for that attack deserve some credit.¶