CVE-2026-71485
Centrifugo: Client-forgeable headers emulation lets any client spoof headers forwarded to proxy backends
Centrifugo is an open-source scalable real-time messaging server. Prior to 6.9.0, Centrifugo copies the client-controlled protocol.ConnectRequest.headers map through OnClientConnecting in internal/client/handler.go, ConnectEvent.Headers, and SetEmulatedHeadersToContext. The requestHeaders path in internal/proxy/http.go, the requestMetadata path in internal/proxy/grpc.go, and the Consume path in internal/unigrpc/grpc.go can forward an allowlisted value as a trusted backend header or metadata value. A remote client can spoof a header such as x-trusted-user for connect, refresh, subscribe, publish, RPC, and related proxy calls when the backend relies on that header for authentication or authorization. The unidirectional gRPC transport has no transport-level HTTP header that can override the emulated value. This issue is fixed in version 6.9.0.
Attack profile
How easy it is to exploit
- Attack vector
- Network
- Complexity
- Low
- Privileges needed
- None
- User interaction
- None
What an attacker can affect
- Confidentiality
- High
- Integrity
- High
- Availability
- None
Exploit likelihood
What each source reports
CVE List Score used
9.1
Centrifugo: Client-forgeable headers emulation lets any client spoof headers forwarded to proxy backends
Centrifugo is an open-source scalable real-time messaging server. Prior to 6.9.0, Centrifugo copies the client-controlled protocol.ConnectRequest.headers map through OnClientConnecting in internal/client/handler.go, ConnectEvent.Headers, and SetEmulatedHeadersToContext. The requestHeaders path in internal/proxy/http.go, the requestMetadata path in internal/proxy/grpc.go, and the Consume path in internal/unigrpc/grpc.go can forward an allowlisted value as a trusted backend header or metadata value. A remote client can spoof a header such as x-trusted-user for connect, refresh, subscribe, publish, RPC, and related proxy calls when the backend relies on that header for authentication or authorization. The unidirectional gRPC transport has no transport-level HTTP header that can override the emulated value. This issue is fixed in version 6.9.0.
- Record
CVE-2026-71485- CVSS 3.1
- 9.1 Critical
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:N - Weakness
- CWE-290
- Updated
- 2026-11-02
EUVDEUVD-2026-63649
9.1
Centrifugo is an open-source scalable real-time messaging server. Prior to 6.9.0, Centrifugo copies the client-controlled protocol.ConnectRequest.headers map through OnClientConnecting in internal/client/handler.go, ConnectEvent.Headers, and SetEmulatedHeadersToContext. The requestHeaders path in internal/proxy/http.go, the requestMetadata path in internal/proxy/grpc.go, and the Consume path in internal/unigrpc/grpc.go can forward an allowlisted value as a trusted backend header or metadata value. A remote client can spoof a header such as x-trusted-user for connect, refresh, subscribe, publish, RPC, and related proxy calls when the backend relies on that header for authentication or authorization. The unidirectional gRPC transport has no transport-level HTTP header that can override the emulated value. This issue is fixed in version 6.9.0.
- Record
EUVD-2026-63649- CVSS 3.1
- 9.1 Critical
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:N - Updated
- 2026-11-02
GitHub AdvisoriesGHSA-9468-v6mj-fppw
9.1
Centrifugo: Client-forgeable headers emulation lets any client spoof headers forwarded to proxy backends
Vulnerability Details
File: internal/client/handler.go (OnClientConnecting), internal/proxy/http.go (requestHeaders), internal/proxy/grpc.go (requestMetadata), internal/unigrpc/grpc.go (Consume)
Line: handler.go:389,548-552 (e.Headers -> SetEmulatedHeadersToContext), http.go:131-165 (requestHeaders), grpc.go:82-114 (requestMetadata), unigrpc/grpc.go:46 (Headers: req.Headers)
Root Cause
The http_headers / grpc_metadata proxy config options are documented as "List of incoming HTTP header names to forward to the proxy backend" / "List of incoming gRPC metadata keys to forward to the proxy backend" -- implying the value originates from the underlying transport connection (e.g. set by a trusted reverse proxy/API gateway in front of Centrifugo). Operators commonly use this to forward identity/trust headers to their own backend for access-control decisions.
However, the wire protocol used by every Centrifugo transport (protocol.ConnectRequest, field headers) includes a headers map populated entirely from the connecting client's own message (its JSON connect command, or for gRPC, its ConnectRequest protobuf field) -- not from real transport-level HTTP headers. Centrifugo copies this client-supplied map verbatim into ConnectEvent.Headers, then into an "emulated headers" context value used by every proxy type (connect, refresh, subscribe, publish, rpc, sub_refresh, map_publish, map_remove, shared_poll_refresh) for the entire connection lifetime. Any header name in the operator's allowlist is forwarded to the backend with the client's own chosen value, unless a header of the exact same name also happens to be present on the genuine underlying HTTP request. For the unidirectional gRPC transport specifically there is no HTTP request at all, so there is no possible override -- the client-controlled value always wins.
Attack Scenario
- Operator configures a connect proxy with
http_headers: ["x-trusted-user"](or any header name their backend uses for caller identity/trust). - Attacker connects directly to Centrifugo and sends a connect command containing
"headers": {"x-trusted-user": "<any value>"}. - Centrifugo forwards
X-Trusted-User: <attacker value>to the backend exactly as if a trusted intermediary had set it. - The backend, trusting this header per its own design, grants whatever access/identity it associates with that value.
Impact
Full spoofing of any header value the backend trusts for authentication/authorization, for every proxy call type, for the lifetime of the connection -- up to full account/identity impersonation depending on backend logic. No credentials, JWT, or API key required.
Vulnerable Code
// internal/proxy/http.go
func requestHeaders(ctx context.Context, allowedHeaders, allowedMetaKeys []string, staticHeaders map[string]string) http.Header {
headers := http.Header{}
for k, v := range staticHeaders { headers.Set(k, v) }
emulatedHeaders, _ := clientcontext.GetEmulatedHeadersFromContext(ctx) // 100% client-controlled
for k, v := range emulatedHeaders {
if slices.Contains(allowedHeaders, strings.ToLower(k)) {
headers.Set(k, v) // forwarded to backend as a real outgoing HTTP header
}
}
httpHeaders, hasHTTPHeaders := middleware.GetHeadersFromContext(ctx) // real headers, if present
for k, vv := range httpHeaders {
if slices.Contains(allowedHeaders, strings.ToLower(k)) {
headers[k] = vv // only overrides if SAME header name was ALSO on the real request
}
}
...
}
// internal/unigrpc/grpc.go -- never goes through HTTP middleware, no competing real-header check at all
connectRequest := &protocol.ConnectRequest{
Token: req.Token, Data: req.Data, Name: req.Name, Version: req.Version,
Headers: req.Headers, // the gRPC client's own protobuf field
}
Recommended Fix
Document explicitly that http_headers/grpc_metadata…
- Record
GHSA-9468-v6mj-fppw- CVSS 3.1
- 9.1 Critical
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:N - Weakness
- CWE-290
- Updated
- 2026-11-02
NVD
9.1
Centrifugo is an open-source scalable real-time messaging server. Prior to 6.9.0, Centrifugo copies the client-controlled protocol.ConnectRequest.headers map through OnClientConnecting in internal/client/handler.go, ConnectEvent.Headers, and SetEmulatedHeadersToContext. The requestHeaders path in internal/proxy/http.go, the requestMetadata path in internal/proxy/grpc.go, and the Consume path in internal/unigrpc/grpc.go can forward an allowlisted value as a trusted backend header or metadata value. A remote client can spoof a header such as x-trusted-user for connect, refresh, subscribe, publish, RPC, and related proxy calls when the backend relies on that header for authentication or authorization. The unidirectional gRPC transport has no transport-level HTTP header that can override the emulated value. This issue is fixed in version 6.9.0.
- Record
CVE-2026-71485- CVSS 3.1
- 9.1 Critical
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:N - Weakness
- CWE-290
- Updated
- 2026-11-02
OSV
9.1
Centrifugo: Client-forgeable headers emulation lets any client spoof headers forwarded to proxy backends
Centrifugo is an open-source scalable real-time messaging server. Prior to 6.9.0, Centrifugo copies the client-controlled protocol.ConnectRequest.headers map through OnClientConnecting in internal/client/handler.go, ConnectEvent.Headers, and SetEmulatedHeadersToContext. The requestHeaders path in internal/proxy/http.go, the requestMetadata path in internal/proxy/grpc.go, and the Consume path in internal/unigrpc/grpc.go can forward an allowlisted value as a trusted backend header or metadata value. A remote client can spoof a header such as x-trusted-user for connect, refresh, subscribe, publish, RPC, and related proxy calls when the backend relies on that header for authentication or authorization. The unidirectional gRPC transport has no transport-level HTTP header that can override the emulated value. This issue is fixed in version 6.9.0.
- Record
CVE-2026-71485- CVSS 3.1
- 9.1 Critical
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:N - Weakness
- CWE-290
- Updated
- 2026-11-02
OSVGHSA-9468-v6mj-fppw
9.1
Centrifugo: Client-forgeable headers emulation lets any client spoof headers forwarded to proxy backends
Vulnerability Details
File: internal/client/handler.go (OnClientConnecting), internal/proxy/http.go (requestHeaders), internal/proxy/grpc.go (requestMetadata), internal/unigrpc/grpc.go (Consume)
Line: handler.go:389,548-552 (e.Headers -> SetEmulatedHeadersToContext), http.go:131-165 (requestHeaders), grpc.go:82-114 (requestMetadata), unigrpc/grpc.go:46 (Headers: req.Headers)
Root Cause
The http_headers / grpc_metadata proxy config options are documented as "List of incoming HTTP header names to forward to the proxy backend" / "List of incoming gRPC metadata keys to forward to the proxy backend" -- implying the value originates from the underlying transport connection (e.g. set by a trusted reverse proxy/API gateway in front of Centrifugo). Operators commonly use this to forward identity/trust headers to their own backend for access-control decisions.
However, the wire protocol used by every Centrifugo transport (protocol.ConnectRequest, field headers) includes a headers map populated entirely from the connecting client's own message (its JSON connect command, or for gRPC, its ConnectRequest protobuf field) -- not from real transport-level HTTP headers. Centrifugo copies this client-supplied map verbatim into ConnectEvent.Headers, then into an "emulated headers" context value used by every proxy type (connect, refresh, subscribe, publish, rpc, sub_refresh, map_publish, map_remove, shared_poll_refresh) for the entire connection lifetime. Any header name in the operator's allowlist is forwarded to the backend with the client's own chosen value, unless a header of the exact same name also happens to be present on the genuine underlying HTTP request. For the unidirectional gRPC transport specifically there is no HTTP request at all, so there is no possible override -- the client-controlled value always wins.
Attack Scenario
- Operator configures a connect proxy with
http_headers: ["x-trusted-user"](or any header name their backend uses for caller identity/trust). - Attacker connects directly to Centrifugo and sends a connect command containing
"headers": {"x-trusted-user": "<any value>"}. - Centrifugo forwards
X-Trusted-User: <attacker value>to the backend exactly as if a trusted intermediary had set it. - The backend, trusting this header per its own design, grants whatever access/identity it associates with that value.
Impact
Full spoofing of any header value the backend trusts for authentication/authorization, for every proxy call type, for the lifetime of the connection -- up to full account/identity impersonation depending on backend logic. No credentials, JWT, or API key required.
Vulnerable Code
// internal/proxy/http.go
func requestHeaders(ctx context.Context, allowedHeaders, allowedMetaKeys []string, staticHeaders map[string]string) http.Header {
headers := http.Header{}
for k, v := range staticHeaders { headers.Set(k, v) }
emulatedHeaders, _ := clientcontext.GetEmulatedHeadersFromContext(ctx) // 100% client-controlled
for k, v := range emulatedHeaders {
if slices.Contains(allowedHeaders, strings.ToLower(k)) {
headers.Set(k, v) // forwarded to backend as a real outgoing HTTP header
}
}
httpHeaders, hasHTTPHeaders := middleware.GetHeadersFromContext(ctx) // real headers, if present
for k, vv := range httpHeaders {
if slices.Contains(allowedHeaders, strings.ToLower(k)) {
headers[k] = vv // only overrides if SAME header name was ALSO on the real request
}
}
...
}
// internal/unigrpc/grpc.go -- never goes through HTTP middleware, no competing real-header check at all
connectRequest := &protocol.ConnectRequest{
Token: req.Token, Data: req.Data, Name: req.Name, Version: req.Version,
Headers: req.Headers, // the gRPC client's own protobuf field
}
Recommended Fix
Document explicitly that http_headers/grpc_metadata…
- Record
GHSA-9468-v6mj-fppw- CVSS 3.1
- 9.1 Critical
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:N - Weakness
- CWE-290
- Updated
- 2026-11-02
OSVGO-2026-6490 No score
Centrifugo: Client-forgeable headers emulation lets any client spoof headers forwarded to proxy backends in github.com/centrifugal/centrifugo
Centrifugo: Client-forgeable headers emulation lets any client spoof headers forwarded to proxy backends in github.com/centrifugal/centrifugo
- Record
GO-2026-6490- Updated
- 2026-11-02
Affected products
Affected packages
| Package | Affected | Fixed |
|---|---|---|
github.com/centrifugal/centrifugoGo |
through 6.8.4 | 6.9.0 |
github.com/centrifugal/centrifugoGo |
before 6.9.0 | 6.9.0 |
github.com/centrifugal/centrifugoGo |
All versions | None known |
github.com/centrifugal/centrifugo/v3Go |
All versions | None known |
github.com/centrifugal/centrifugo/v4Go |
All versions | None known |
github.com/centrifugal/centrifugo/v5Go |
All versions | None known |
github.com/centrifugal/centrifugo/v6Go |
before 6.9.0 | 6.9.0 |
History
| Recorded | Changed | Sources |
|---|---|---|
| first indexed | CVE List, EUVD, GitHub Advisories, NVD, OSV |
Dates are when Every CVE recorded each change. Upstream dates may differ.
In short
CVE-2026-71485 is a critical vulnerability (CVSS 9.1) in Centrifugal Centrifugo, published August 20, 2026. No source lists public exploit code. EPSS puts the chance of exploitation in the next 30 days at 0.61%, the 49th percentile. Fixed in github.com/centrifugal/centrifugo 6.9.0.
References
github.com/centrifugal/centrifugo/security/advisories/GHSA-9468-v6mj-fppw
github.com/centrifugal/centrifugo/pull/1182
github.com/centrifugal/centrifugo/commit/84d38cea1dd2efa24375a148817a974c8727f4b0
github.com/centrifugal/centrifugo/releases/tag/v6.9.0
nvd.nist.gov/vuln/detail/CVE-2026-71485
github.com/advisories/GHSA-9468-v6mj-fppw
github.com/CVEProject/cvelistV5/tree/main/cves/2026/71xxx/CVE-2026-71485.json
github.com/centrifugal/centrifugo
Left out here: the rest of 2 source descriptions. The JSON record has them.