Skip to content

fix: prevent CRLF injection through chunk trailers and extensions (#1256) - #1270

Open
pjfanning wants to merge 1 commit into
apache:1.4.xfrom
pjfanning:crlf-14
Open

fix: prevent CRLF injection through chunk trailers and extensions (#1256)#1270
pjfanning wants to merge 1 commit into
apache:1.4.xfrom
pjfanning:crlf-14

Conversation

@pjfanning

Copy link
Copy Markdown
Member

cherry pick b9a6a41 #1256

Motivation:
RenderSupport.renderChunk emitted two attacker-influenced parts of a chunked response without the CR/LF guard that the main header block relies on:

  • Trailer headers were rendered with r ~~ trailer, which resolves to the generic sequence renderer and calls header.render directly, bypassing the ~~(HttpHeader) overload whose check scans the rendered bytes for CR/LF and drops the header. HttpEntity.LastChunk and RawHeader do no CR/LF validation, so a trailer built from user data (e.g. RawHeader("X-Trace", "ok\r\nSet-Cookie: ..."))) could split the response. The identical header placed in the main header block is caught; only the trailer path let it through.
  • The chunk extension was rendered raw into the chunk-size line (r ~~ ';' ~~ extension) with no CR/LF check, so a CR/LF in an app-set extension corrupted the chunk framing.

Modification:
Render each trailer header through the guarded ~~(HttpHeader) overload (trailer.foreach(r ~~ _)), matching the main header block, and remove the now-unused trailerRenderer implicit so the unguarded path cannot be reintroduced by accident. Skip a chunk extension that contains CR/LF; the extension is optional metadata, so omitting an illegal one is safe. Byte output is unchanged for valid trailers and extensions.

Result:
CR/LF in a chunk trailer header value or a chunk extension can no longer reach the wire; the offending header/extension is dropped, as in the main header block, instead of splitting the response.

Tests:

  • sbt "http-core/testOnly org.apache.pekko.http.impl.engine.rendering.ResponseRendererSpec org.apache.pekko.http.impl.engine.rendering.RequestRendererSpec" - pass (62 tests); two new tests assert a CRLF-bearing trailer header and a CRLF-bearing chunk extension are dropped. Verified both fail with the fix stashed (the injected bytes reach the output).
  • sbt http-core/mimaReportBinaryIssues - pass (internal impl.engine.rendering change, no public API).

References:
None - closes the CRLF-injection paths in chunked response rendering

…ache#1256)

Motivation:
`RenderSupport.renderChunk` emitted two attacker-influenced parts of a
chunked response without the CR/LF guard that the main header block
relies on:

- Trailer headers were rendered with `r ~~ trailer`, which resolves to
  the generic sequence renderer and calls `header.render` directly,
  bypassing the `~~(HttpHeader)` overload whose `check` scans the
  rendered bytes for CR/LF and drops the header. `HttpEntity.LastChunk`
  and `RawHeader` do no CR/LF validation, so a trailer built from user
  data (e.g. `RawHeader("X-Trace", "ok\r\nSet-Cookie: ..."))`) could
  split the response. The identical header placed in the main header
  block is caught; only the trailer path let it through.
- The chunk extension was rendered raw into the chunk-size line
  (`r ~~ ';' ~~ extension`) with no CR/LF check, so a CR/LF in an
  app-set extension corrupted the chunk framing.

Modification:
Render each trailer header through the guarded `~~(HttpHeader)`
overload (`trailer.foreach(r ~~ _)`), matching the main header block,
and remove the now-unused `trailerRenderer` implicit so the unguarded
path cannot be reintroduced by accident. Skip a chunk extension that
contains CR/LF; the extension is optional metadata, so omitting an
illegal one is safe. Byte output is unchanged for valid trailers and
extensions.

Result:
CR/LF in a chunk trailer header value or a chunk extension can no
longer reach the wire; the offending header/extension is dropped, as
in the main header block, instead of splitting the response.

Tests:
- sbt "http-core/testOnly org.apache.pekko.http.impl.engine.rendering.ResponseRendererSpec org.apache.pekko.http.impl.engine.rendering.RequestRendererSpec" - pass (62 tests); two new tests assert a CRLF-bearing trailer header and a CRLF-bearing chunk extension are dropped. Verified both fail with the fix stashed (the injected bytes reach the output).
- sbt http-core/mimaReportBinaryIssues - pass (internal impl.engine.rendering change, no public API).

References:
None - closes the CRLF-injection paths in chunked response rendering
@pjfanning pjfanning added this to the 1.4.1 milestone Sep 3, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant