WebSocket compression can turn reflected data into a leak

WebSocket applications often compress messages to reduce bandwidth. When that compression uses shared history across messages, the encoded size depends on earlier plaintext. If an attacker can place chosen text into the same compression context as a secret and observe the resulting message length, the length may reveal information about the secret.

RFC 7692 specifies the permessage-deflate extension. Its security considerations explicitly point to the known exploit when history-based compression is combined with a secure transport. Encryption protects the bytes on the wire. It does not remove the relationship between message content and compressed length.

The trust boundary

The relevant condition is not simply "WebSocket is enabled." It is whether attacker-controlled and confidential data share one compression context, and whether the attacker can observe message sizes or timing. A chat client that compresses a whole conversation can mix a user's message with other users' content. A browser automation service that returns both reflected input and a secret page can create the same pattern. A notification channel that compresses repeated structured messages may expose whether a guessed field is present.

TLS-level compression is widely disabled for this reason. RFC 9325 reiterates the compression-related risk and says implementations and deployments should not support TLS compression unless the application protocol is proven safe. RFC 9114 carries the same concern into HTTP content compression: confidential and attacker-controlled data should not share a compression context without separate contexts.

permessage-deflate can be configured with context takeover enabled or disabled. Disabling takeover limits the influence of one message on the next, but it does not automatically make a protocol safe. The application must still account for secrets and attacker input within a single message, for side channels in message sizes, and for intermediaries that recompress content.

Where to inspect

List every WebSocket endpoint and its compression negotiation. Record whether the extension is accepted, whether client and server context takeover are enabled, the window size, and the maximum message size. Then identify the data classes in each message. A public status stream carrying no secrets is different from an authenticated collaboration session that mixes tenant content.

Review the proxy chain. A proxy may add compression even when the application does not. It may also buffer a message and make size observations less visible to the application. Check response and request compression at the edge, in the application server, and in any gateway that transforms WebSocket frames.

Inspect application code for reflection. Error pages, search results, markup sanitizers, and template rendering can echo requests. If the echoed string enters a compressed frame alongside a token, permission value, or private record, the combined message becomes the sensitive boundary.

Reduce exposure

The simplest option is to decline the extension for endpoints that mix secrets with untrusted content. If compression is needed for a high-volume stream, use a separate connection or compression context for public data and authenticated data. Avoid compressing secrets and reflected input in the same message. Do not rely only on masking WebSocket frame payloads; masking prevents cache poisoning in intermediaries and does not address compression history.

Add message-size and rate limits that account for decompression work. A small compressed frame can expand substantially. The server's parser should enforce a plaintext limit before allocating memory and should close a connection that exceeds the policy. This is a resource-control measure, not a replacement for side-channel analysis.

Test with differential inputs. In a staging environment, send a benign string and then a string that is a prefix of a protected value. Compare frame lengths and timing under the production compression settings. Test client-to-server and server-to-client directions separately. Repeat behind the actual CDN and reverse proxy.

Defensive baseline

Document the compression policy per endpoint. Default to no shared history when messages contain mixed trust levels. Keep secrets out of templates that reflect attacker input. Enforce decompressed-size limits and alert on abnormal compression ratios. Recheck the policy whenever a framework enables an extension by default.

Compression is useful when the data model is understood. It becomes dangerous when a performance setting silently puts an attacker at the same table as a secret.

References