Skip to content

GHSA-7ghq-v6jf-g56c CVE-2026-88012 CVE-2026-88878 GO-2026-6454

Traefik: respondingTimeouts.readTimeout is not applied to HTTP/3, leaving slow-body uploads unbounded

Medium Go
Severity
6.9 / 10
Medium · CVSS 4.0
Exploit chance (EPSS)
0.52%
Chance of exploitation in the next 30 days. Higher than 43.92% of scored vulnerabilities.
Known exploited
No
Not in CISA's catalog
Weakness
CWE-770 Allocation of Resources Without Limits or Throttling
Fix
Available
2 packages have a fixed version

Description

Summary

There is a medium severity vulnerability in Traefik's HTTP/3 entry points: the respondingTimeouts settings were not applied to the HTTP/3 request path. readTimeout in particular is on by default at 60s and is documented as bounding the time to read the entire request including its body, but it is enforced as a deadline on the TCP connection, which cannot reach a QUIC stream, and Traefik's HTTP/3 server was constructed with no timeout of any kind. An unauthenticated client that trickles a request body therefore holds a request open for as long as it chooses, and with it one upstream connection per request, at negligible cost to itself. Backends with bounded connection pools are the practical pressure point.

The HTTP/3 path lost these timeouts in v2.8.2, when a quic-go API change removed the embedded http.Server that had carried them; every release from v2.8.2 onward is affected, and releases before v2.8.2 are not. Traefik v2.8.2 through v2.10.x and v3.0 through v3.6 are affected and are no longer maintained: they will not receive a patch on their own line, and the remedy for their users is to upgrade to v2.11.56 or v3.7.12.

Patches

For more information

If you have any questions or comments about this advisory, please open an issue.

<details> <summary>Original Description</summary>

Summary

entryPoints.<name>.transport.respondingTimeouts.readTimeout is documented as:

"Set the timeouts for incoming requests to the Traefik instance. This is the maximum duration for reading the entire request, including the body." — Default: 60s

It is on by default and it works over HTTP/1.1 and HTTP/2. It has no effect on HTTP/3.

The consequence is not that a hardening option was left unset. It is that every Traefik deployment with http3 enabled carries a 60-second bound that the operator has every reason to believe is in force, and which is silently absent on that protocol. A single client trickling one body byte every few seconds holds a request open indefinitely, and with it one upstream connection per request.

readTimeout is applied as a deadline on the TCP connection. HTTP/3 does not have one, and Traefik's HTTP/3 server is constructed with no timeout of any kind.

Steps to reproduce

No containers, VMs or cloud services — the official release binary, curl, openssl, and a 68-line Python standard-library backend. Everything is attached.

bash reproduce.sh                 # readTimeout 5s, ~40 seconds
MODE=default bash reproduce.sh    # the stock 60s default, ~4 minutes

By hand:

1. Static config (conf/traefik.yml, complete and unredacted). Note there is no respondingTimeouts block at all — this is the documented 60s default:

global:
  checkNewVersion: false
  sendAnonymousUsage: false

log:
  level: DEBUG

entryPoints:
  websecure:
    address: ":8443"
    http3:
      advertisedPort: 8443

providers:
  file:
    filename: conf/dynamic.yml

api:
  dashboard: false

2. Dynamic config (conf/dynamic.yml):

http:
  routers:
    backend-router:
      rule: "PathPrefix(`/`)"
      service: backend-svc
      entryPoints: [websecure]
      tls: {}
  services:
    backend-svc:
      loadBalancer:
        servers:
          - url: "http://127.0.0.1:8080"
tls:
  certificates:
    - certFile: cert.pem
      keyFile: key.pem

3. A self-signed cert, so no CA install and no sudo:

openssl req -x509 -newkey rsa:2048 -keyout key.pem -out cert.pem -days 30 -nodes \
    -subj "/CN=localhost" -addext "subjectAltName=DNS:localhost,IP:127.0.0.1"

4. A backend that reads the request body before responding, as a real HTTP/1.1 server does (backend.py, standard library only). This matters: a backend that answers the request headers alone lets Traefik release the upstream connection immediately, which hides the behaviour entirely.

python3 backend.py 8080 &
./traefik --configFile=conf/traefik.yml

5. The same slow upload over each protocol. The hold must exceed the timeout under test, so 92 seconds against the 60-second default:

{ for i in $(seq 1 23); do printf 'x'; sleep 4; done; } | \
    curl -v -k -T - --http1.1     https://localhost:8443/

{ for i in $(seq 1 23); do printf 'x'; sleep 4; done; } | \
    curl -v -k -T - --http3-only  https://localhost:8443/

Result

Traefik v3.7.10 (langres, go1.26.5), official traefik_v3.7.10_linux_amd64.tar.gz, sha256 01811bb12d44f17280550f425f5e3128d6c325f2665c09e67a651ca535f490ce.

Upstream connection lifetime measured at the backend — the client's view only shows the absence of a server action, which proves nothing on its own:

config hold HTTP/1.1 (control) HTTP/3
stock — documented 60s default 92 s 59.99 s — released at the documented default 92.94 s — held for the entire window
readTimeout: 5s 30 s 4.99 s 30.98 s

The HTTP/1.1 column is the control and it is the point of the exercise: the timeout is demonstrably live on this exact binary, releasing the upstream at its configured value. The HTTP/3 request, in the same run against the same instance with the same setting, ran to completion with the upstream pinned throughout. Traefik returned 499 on the aborted HTTP/1.1 arm and took no action at all on HTTP/3 — no RST_STREAM, no H3_REQUEST_INCOMPLETE, no connection close.

The setting is not being silently discarded: Traefik's own DEBUG log prints the loaded static configuration including "respondingTimeouts":{"idleTimeout":"3m0s","readTimeout":"5s"}. Full curl -v output, backend logs and Traefik DEBUG logs for every arm are in evidence/.

Cause

readTimeout is a TCP connection deadline — pkg/server/server_entrypoint_tcp.go:273:

if e.transportConfiguration.RespondingTimeouts.ReadTimeout > 0 {
    err := writeCloser.SetReadDeadline(time.Now().Add(time.Duration(e.transportConfiguration.RespondingTimeouts.ReadTimeout)))

A deadline on a TCP connection cannot reach a QUIC stream.

And the HTTP/3 server is given no timeout of any kind — pkg/server/server_entrypoint_tcp_http3.go:65:

h3.Server = &http3.Server{
    Addr:      config.GetAddress(),
    Port:      config.HTTP3.AdvertisedPort,
    Handler:   httpsServer.Server.(*http.Server).Handler,
    TLSConfig: &tls.Config{GetConfigForClient: h3.getTLSConfigForClient},
    QUICConfig: &quic.Config{
        Allow0RTT: false,
    },
    ConnContext: func(ctx context.Context, c *quic.Conn) context.Context {

It reuses the HTTPS server's handler and inherits none of its timeouts. There is therefore no duration control on the HTTP/3 request path at all — not readTimeout, not idleTimeout, nothing.

Note that this cannot be fixed by passing a field through: quic-go's http3.Server exposes no request-read deadline. The remedy has to be enforced around the request body inside the handler, or upstream in quic-go.

Suggested remedy

In preference order:

  1. Enforce readTimeout on the HTTP/3 path in the handler. Traefik already passes the HTTPS server's handler to http3.Server, so when RespondingTimeouts.ReadTimeout > 0 it can wrap r.Body for HTTP/3 requests in a reader that enforces the deadline.
  2. Add a request-read deadline upstream in quic-go's http3.Server and pass it through.
  3. At minimum, document it. See below — the current documentation does not tell an operator this.

A sketch of option 1

Offered as a description of the shape, not as a patch. I have not built or tested this against Traefik, and I am not going to present untested code as though I had. If a working, tested patch would be useful, say so and I will prepare one properly and verify it against the reproducer above.

Where the HTTP/3 handler is wired up — server_entrypoint_tcp_http3.go, around the http3.Server construction — the handler passed in could be wrapped so that, when ReadTimeout is configured, an HTTP/3 request body carries the same deadline the TCP path gets from SetReadDeadline:

// Roughly: for HTTP/3 requests only, and only when the timeout is set.
if readTimeout > 0 && r.ProtoMajor == 3 && r.Body != nil {
    r.Body = deadlineBody(r.Body, readTimeout)
}

The part worth knowing, because it is what makes the approach work rather than merely look tidy: closing the request body cancels the QUIC stream read. In quic-go, http3's body Close() calls str.CancelRead(...), which unblocks a Read that is already parked waiting on the client. So a timer that closes the body on expiry bounds both a client that trickles and a client that simply stops sending — the latter being the case a wrapper that only checks the clock on each returning Read would miss entirely.

Returning os.ErrDeadlineExceeded from the wrapped Read keeps the failure classified as a timeout rather than a client abort, which matters for whatever status code and logging you decide is right.

Two design questions I would not want to answer on your behalf: whether HTTP/3 should reuse respondingTimeouts.readTimeout or get its own setting, and whether enforcement belongs in the handler wrapper or somewhere closer to the entrypoint. Both are your call.

A documentation issue, separately

Two things in the docs are worth correcting regardless of how the code question is resolved.

1. The readTimeout description carries no protocol qualification. It says "the maximum duration for reading the entire request, including the body", which is exactly what an operator relies on. The entrypoint page does note that respondingTimeouts have "no effect for UDP entryPoints", but that does not cover this case: HTTP/3 here is served on an HTTP entrypoint with an http3: block, not on a Traefik UDP entrypoint, which is a separate feature for UDP routers. An operator who adds http3: to their existing HTTPS entrypoint has not created a UDP entrypoint and has no reason to read that caveat as applying to them.

2. SECURITY.md's supported-versions table is stale. It lists 3.6.x as supported and < 3.6.x as unsupported, while 3.7.10 is the current release. Anyone checking whether their version is in scope before reporting gets a confusing answer.

Impact

Each held request occupies one upstream connection for as long as the client chooses, at negligible cost to the client, and the same client can open many. Backends with bounded connection pools are the practical pressure point.

I have not measured a concurrency ceiling on Traefik itself, so I am not asserting one. If that number matters to your assessment, tell me and I will measure it.

LLM ("AI") use disclosure

Not required by your policy, but stated because it is true and you should be able to weigh it. I am a penetration tester, not a Go developer. The finding, the attack concept and the decision to measure the upstream leg rather than the client are mine. An LLM coding assistant (Claude) built the test harness, ran the matrix, and located the two source citations; I verified those by hand against the v3.7.10 tag and ran the reproducer myself. I am a human and I will be the one replying in this thread.

Disclosure

Bishop Fox operates a 90-day disclosure policy, starting the day this is submitted, with extensions where a fix is in progress. Tell me what you would prefer and I will work to it.

Environment

  • Traefik v3.7.10, official traefik_v3.7.10_linux_amd64.tar.gz, sha256 01811bb12d44f17280550f425f5e3128d6c325f2665c09e67a651ca535f490ce
  • Kali GNU/Linux (WSL2), kernel 6.6.114.1
  • curl 8.19.0 with ngtcp2 1.21.0 / nghttp3 1.15.0### Summary Short summary of the problem. Make the impact and severity as clear as possible. For example: An unsafe deserialization vulnerability allows any unauthenticated user to execute arbitrary code on the server.

Details

Give all details on the vulnerability. Pointing to the incriminated source code is very helpful for the maintainer.

PoC

Complete instructions, including specific configuration details, to reproduce the vulnerability.

Impact

What kind of vulnerability is it? Who is impacted?

</details> ---

Affected versions

Go

PackageVulnerableFixed in
github.com/traefik/traefik
all versions
No fix yet
github.com/traefik/traefik/v2
>= 2.8.2, < 2.11.56
2.11.56
github.com/traefik/traefik/v3
>= 3.0.0, < 3.7.12
3.7.12

How to fix

Move to the fixed version or later with your package manager, update the lockfile, and run your tests. If the package only arrives through another dependency, upgrade that dependency first. If none of its releases pulls a fixed version yet, force the version with your package manager's override.

github.com/traefik/traefik/v2 Go → 2.11.56

go
go get github.com/traefik/traefik/v2@v2.11.56 && go mod tidy

github.com/traefik/traefik/v3 Go → 3.7.12

go
go get github.com/traefik/traefik/v3@v3.7.12 && go mod tidy

Severity breakdown

CVSS 4.0 6.9

CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:N/VA:L/SC:N/SI:N/SA:N/E:X/CR:X/IR:X/AR:X/MAV:X/MAC:X/MAT:X/MPR:X/MUI:X/MVC:X/MVI:X/MVA:X/MSC:X/MSI:X/MSA:X/S:X/AU:X/R:X/V:X/RE:X/U:X

Attack vector
Network
Attack complexity
Low
Attack requirements
None
Privileges required
None
User interaction
None
Confidentiality (vulnerable system)
None
Integrity (vulnerable system)
None
Availability (vulnerable system)
Low
Confidentiality (subsequent systems)
None
Integrity (subsequent systems)
None
Availability (subsequent systems)
None
Exploit maturity
Not defined
Scope
X

CVSS 3.1 5.3

CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:L

Attack vector
Network
Attack complexity
Low
Privileges required
None
User interaction
None
Scope
Unchanged
Confidentiality
None
Integrity
None
Availability
Low

Weakness

CWE-770: Allocation of Resources Without Limits or Throttling

The product allocates a reusable resource or group of resources on behalf of an actor without imposing any intended restrictions on the size or number of resources that can be allocated.

How to prevent it
  • Requirements: Clearly specify the minimum and maximum expectations for capabilities, and dictate which behaviors are acceptable when resource allocation reaches limits.
  • Architecture and Design: Limit the amount of resources that are accessible to unprivileged users. Set per-user limits for resources. Allow the system administrator to define these limits. Be careful to avoid CWE-410.
  • Architecture and Design: Design throttling mechanisms into the system architecture. The best protection is to limit the amount of resources that an unauthorized user can cause to be expended. A strong authentication and access control model will help prevent such attacks from occurring in the first place, and it will help the administrator to identify who is committing the abuse. The login application should be protected against DoS attacks as much as possible. Limiting the database access, perhaps by caching result sets, can help minimize the resources expended. To further limit the potential for a DoS attack, consider tracking the rate of requests received from users and blocking requests that exceed a defined rate threshold.
  • Implementation: Assume all input is malicious. Use an "accept known good" input validation strategy, i.e., use a list of acceptable inputs that strictly conform to specifications. Reject any input that does not strictly conform to specifications, or transform it into something that does. When performing input validation, consider all potentially relevant properties, including length, type of input, the full range of acceptable values, missing or extra inputs, syntax, consistency across related fields, and conformance to business rules. As an example of business rule logic, "boat" may be syntactically valid because it only contains alphanumeric characters, but it is not valid if the input is only expected to contain colors such as "red" or "blue." Do not rely exclusively on looking for malicious or malformed inputs. This is likely to miss at least one undesirable input, especially if the code's environment changes. This can give attackers enough room to bypass the intended validation. However, denylists can be useful for detecting potential attacks or determining which inputs are so malformed that they should be rejected outright.
  • Architecture and Design: For any security checks that are performed on the client side, ensure that these checks are duplicated on the server side, in order to avoid CWE-602. Attackers can bypass the client-side checks by modifying values after the checks have been performed, or by changing the client to remove the client-side checks entirely. Then, these modified values would be submitted to the server.
  • Architecture and Design: Mitigation of resource exhaustion attacks requires that the target system either: recognizes the attack and denies that user further access for a given amount of time, typically by using increasing time delays uniformly throttles all requests in order to make it more difficult to consume resources more quickly than they can again be freed. The first of these solutions is an issue in itself though, since it may allow attackers to prevent the use of the system by a particular valid user. If the attacker impersonates the valid user, they may be able to prevent the user from accessing the server in question. The second solution can be difficult to effectively institute -- and even when properly done, it does not provide a full solution. It simply requires more resources on the part of the attacker.
  • Architecture and Design: Ensure that protocols have specific limits of scale placed on them.
  • Architecture and Design, Implementation: If the program must fail, ensure that it fails gracefully (fails closed). There may be a temptation to simply let the program fail poorly in cases such as low memory conditions, but an attacker may be able to assert control before the software has fully exited. Alternately, an uncontrolled failure could cause cascading problems with other downstream components; for example, the program could send a signal to a downstream process so the process immediately knows that a problem has occurred and has a better chance of recovery. Ensure that all failures in resource allocation place the system into a safe posture.
  • Operation, Architecture and Design: Use quotas or other resource-limiting settings provided by the operating system or environment. For example, when managing system resources in POSIX, setrlimit() can be used to set limits for certain types of resources, and getrlimit() can determine how many resources are available. However, these functions are not available on all operating systems. When the current levels get close to the maximum that is defined for the application (see CWE-770), then limit the allocation of further resources to privileged users; alternately, begin releasing resources for less-privileged users. While this mitigation may protect the system from attack, it will not necessarily stop attackers from adversely impacting other users. Ensure that the application performs the appropriate error checks and error handling in case resources become unavailable (CWE-703).

References

Timeline

  1. Published
  2. Last updated by the source
  3. Latest EPSS score
AdvisorySeverityWhy it's related
GO-2026-6558
Traefik: BasicAuth singleflight coalescing reintroduces an unauthenticated username-enumeration timing oracle
Medium Also affects github.com/traefik/traefik
GHSA-f52w-8j3h-j724
Traefik: Rootless HTTP/1 request-target routes as "/" but is forwarded verbatim, bypassing path-scoped routing, middleware guards and access logging
High Also affects github.com/traefik/traefik
GHSA-w4v4-9rw7-5326
Traefik: Inconsistent Interpretation of HTTP Requests ('HTTP Request/Response Smuggling') and Incorrect Authorization
Critical Also affects github.com/traefik/traefik
GHSA-qqjf-53cj-pwvv
Traefik HTTP/3 Backend NTLM Connection Reuse
Critical Also affects github.com/traefik/traefik
GO-2026-6467
Traefik entrypoint header-name sanitization bypassed via request trailers
High Also affects github.com/traefik/traefik
GHSA-rf44-j88r-hh8c
Traefik: ForwardAuth identity spoofing via dot-form header alias
High Also affects github.com/traefik/traefik
GHSA-62fc-8686-hfmq
Traefik: `allowCrossNamespace=false` bypass via `@kubernetescrd` TraefikService backendRef
Medium Also affects github.com/traefik/traefik
GO-2026-6209
Traefik: Gateway API route identity collision allows cross-namespace backend hijacking
High Also affects github.com/traefik/traefik
Is this in your code?

MergeHarbor checks every repository's dependencies against this database, ranks what matters by exploitability and reachability, routes it to the owning team and opens the fix pull request.

Check your repos with MergeHarbor