HTTP/1 uses CRLF as a line terminator, but permits implementations to accept a bare LF as a terminator in certain locations. It does not permit a bare LF to be used in the chunked encoding, however. (See https://www.rfc-editor.org/errata/eid7633, in particular the notes on why the proposed errata was rejected.)

We reject bare LFs ending chunk-data lines, but accept them in chunk-size lines. This can, if combined with an implementation that incorrectly permits a bare CR in a chunk-ext, permit request smuggling.

We should reject bare LFs in chunk-data lines.

This is a PUBLIC track security issue and CVE-2025-22871.

Comment From: gopherbot

Change https://go.dev/cl/652998 mentions this issue: net/http: reject newlines in chunk-size lines

Comment From: gabyhelp

Related Code Changes

(Emoji vote if this was helpful or unhelpful; more detailed feedback welcome in this discussion.)

Comment From: neild

@gopherbot please open backport issues for this security fix.

Comment From: gopherbot

Backport issue(s) opened: #72010 (for 1.23), #72011 (for 1.24).

Remember to create the cherry-pick CL(s) as soon as the patch is submitted to master, according to https://go.dev/wiki/MinorReleases.

Comment From: tsuna

Can you clarify what you meant by "This can, if combined with an implementation that incorrectly permits a bare CR in a chunk-ext, permit request smuggling." — which "implementation" are we talking about here?

Comment From: neild

If you have one HTTP implementation which permits a bare LF in the chunk-ext, and another that interprets a bare LF as a line terminator, you can smuggle a request through a proxy using one implementation to a server using the other.

You send a request body consisting of something like: length, ; (starting a chunk-ext), LF (implementation 1 thinks this is part of the chunk-ext, implementation 2 thinks this is the end of the chunk-size line), some carefully-chosen number of bytes (implementation 1 thinks this is the chunk-ext, implementation 2 thinks this is the body), CR LF 0 CR LF (implementation 1 thinks this is chunk-data, implementation 2 thinks this is the last-chunk), and headers for a new request (implementation 1 thinks this is chunk-data, implementation 2 thinks this is a new request).

Permitting an LF in the chunk-ext is a very uncommon bug.

Comment From: gopherbot

Change https://go.dev/cl/657216 mentions this issue: [release-branch.go1.23] net/http: reject newlines in chunk-size lines

Comment From: gopherbot

Change https://go.dev/cl/657056 mentions this issue: [release-branch.go1.24] net/http: reject newlines in chunk-size lines

Comment From: FiloSottile

This can, if combined with an implementation that incorrectly permits a bare CR in a chunk-ext, permit request smuggling.

Just to make sure I am parsing this correctly (pun not intended), this was meant to be "bare LF", not "bare CF", right?

Comment From: neild

Just to make sure I am parsing this correctly (pun not intended), this was meant to be "bare LF", not "bare CF", right?

Yes, sorry. If an implementation permits a bare LF in the chunk-ext (very uncommon), then request smuggling can occur between it and pre-fix net/http.

Comment From: DemiMarie

Does permitting a bare LF to end the chunk data (instead of the required CRLF) also permit request smuggling?

Comment From: neild

Does permitting a bare LF to end the chunk data (instead of the required CRLF) also permit request smuggling?

I don't think so, but I might well be missing something.

Comment From: DemiMarie

@neild Can you provide any specific examples of reverse proxies that have this bug? NGINX allows chunk extensions to end with bare LF and I am wondering if there is a practically exploitable vulnerability here.

Comment From: neild

I'm afraid I don't have any examples I can share.

Comment From: jkaurredhat

@neild Hey, Can this be backported to 1.22 as well ?

Comment From: seankhliao

No, only the 2 most recent major releases are supported.

Comment From: antbob

@jkaurredhat for what its worth the .23 patch Commit 15e01a2 applies cleanly on top of 1.20.14 and almost cleanly on top of 1.18.10 (needs manual apply in one of the tests) so you should have no problem with 1.22 either.

Comment From: parinmaru2

Hey folks, checking to see if the fix for this CVE CVE-2025-22871 going to be part of v1.25.0? Or it is also available in any go v.124.x?

Comment From: neild

This was fixed in Go 1.24.2 and 1.23.8.