September 29, 2020·4 min read·engineeringrubysecurityhttpwebrick

WEBrick tightens Transfer-Encoding parsing to prevent request ambiguity

CVE-2020-25613 concerns disagreement between HTTP parsers. WEBrick 1.6.1 replaces substring matches with whole-value checks for transfer coding and connection handling.

An HTTP proxy and its backend must agree on where a request ends. If they interpret a framing header differently, the same bytes can describe different request boundaries at each layer. Ruby's September 29 advisory identifies that risk in WEBrick's tolerant handling of an invalid Transfer-Encoding value. The issue is CVE-2020-25613, and the recommended correction is WEBrick 1.6.1 or later.

The advisory describes a potential interaction with some HTTP proxies, without declaring every proxy deployment exploitable. Its affected versions include WEBrick 1.6.0 and earlier, along with copies bundled in Ruby 2.5.8, 2.6.6 and 2.7.1 or earlier. This is a component update whose relevance depends on the server actually loaded by the application.

I find Ruby's regular expressions convenient for compact text processing. A protocol parser needs a more exact contract than finding an interesting word in a string. The patch is a useful example because the difference between substring matching and whole-value matching changes which framing algorithm the server selects.

Transfer coding selects how the body is read

The 1.6.1 request implementation checks transfer-encoding before its content-length path. When the field indicates the supported chunked coding, it invokes the chunked-body reader. Unsupported values raise a not-implemented response instead of falling through to ordinary content-length handling.

That branch is part of interpreting the wire protocol. Chunked bodies contain their own size markers and termination structure. A content-length body instead relies on a declared count of body bytes. Selecting the wrong interpretation can change which bytes the server considers body data and which remain for another request.

RFC 7230, published in 2014, defines message-body-length precedence and discusses request smuggling when framing information conflicts. It gives Transfer-Encoding precedence over Content-Length and says an intermediary forwarding such a message must remove the received Content-Length. These are protocol rules for maintaining one interpretation across hops.

The WEBrick defect concerns accepting an invalid coding value too broadly. The important comparison is therefore between the values the backend recognizes and those the proxy recognizes. A backend can correctly decode a legitimate chunked body and still create an unsafe interaction if it selects that decoder for malformed input another parser handles differently.

The patch anchors the regular expression

The release diff changes the chunked test from /chunked/io to /\Achunked\z/io. Previously, the expression succeeded when the word appears somewhere in the value. The replacement requires the value presented to this check to match the supported coding as a whole.

The case-insensitive option remains, so this is not a change requiring one capitalization. The added boundaries are the substantive correction. They prevent an unrelated prefix or suffix from being accepted merely because it contains the expected word.

That is a narrow and useful description of what ships. The patch does not implement every possible transfer coding. It makes the supported branch more exact and retains the error path for values that do not match. A parser can be restrictive about its supported inputs without pretending to understand all extensions.

A short Ruby illustration shows the changed matching rule independently of any network request. These are synthetic parser inputs:

whole_value = /\Achunked\z/i
puts !!(whole_value =~ "chunked")
puts !!(whole_value =~ "CHUNKED")
puts !!(whole_value =~ "prefix-chunked-suffix")

The first two values match and the third does not. This illustrates the predicate, rather than constructing a smuggled request. The security consequence comes from where WEBrick uses that predicate to choose its body reader.

Connection handling receives the same treatment

The diff also anchors the checks for close and keep-alive in the Connection field. These checks determine whether WEBrick keeps the connection active. Substring matching previously allowed a larger value containing one of those words to trigger the corresponding branch.

Connection persistence is relevant because multiple requests can share a connection. A parser's decision about body completion and its decision about reusing the stream both affect how remaining bytes are handled. The patch makes the recognized values explicit at both decision points.

This does not establish a universal rule that every HTTP field should be parsed as one word. Fields have different grammars. The concrete change is to these existing WEBrick branches, where a broad search for a supported token was too permissive. Correctness depends on the grammar and implementation contract of the field being interpreted.

Chunk reading remains a separate operation

The chunked-body reader obtains a chunk size, reads the corresponding bytes and checks that the requested chunk data was present. It then advances through later chunks until the terminating size. Afterward, it reads the trailers and marks the remaining size as zero.

Those operations depend on reaching the reader through the right header interpretation. A precise chunk-data loop does not compensate for an ambiguous selection rule above it. The patch therefore repairs the classification boundary before body decoding begins.

I prefer that explanation to treating request smuggling as an obscure payload trick. The underlying requirement is agreement about message boundaries across components. A proxy and backend participate in one request path, so a permissive local parser can create an error only visible when the parsers are composed.

The content-length branch maintains a remaining-byte count as it delivers data to the caller. If the socket ends before that count reaches zero, it reports an invalid body size. This is another explicit boundary check in the same implementation, separate from selecting the appropriate decoder.

The gem version is the corrective boundary

The advisory gives gem update webrick as an update route and provides gem "webrick", ">= 1.6.1" for Bundler-managed applications. The relevant implementation is the WEBrick gem selected by the running process. A runtime that originally bundled an affected copy can use an updated component through its dependency setup.

The release changes the version constant alongside the parser checks. That gives consumers a version identifying the stricter implementation. The practical correction is loading that implementation in the server process, where the header value determines body framing and connection behavior.