Side channels in encrypted protocols
Compression oracles, TLS record lengths, QUIC behavior, and why encrypted protocols still leak through observable structure.
Modern encrypted protocols protect content but expose structure. TLS records have observable lengths and arrival times. Compressed payloads expose ratios that depend on shared compression context. QUIC encrypts the transport but exposes packet numbers, frame boundaries, and timing patterns. Each of these structural exposures is a side channel — an unintended information leak through observable behavior of the encrypted protocol.
Side channels are different from passive flow analysis (covered in earlier modules) in that they exploit specific structural properties of the protocol, often with surprising effectiveness. The CRIME and BREACH attacks of the 2010s used compression side channels to recover secret cookies from encrypted HTTPS traffic without ever decrypting anything. Modern variants continue to surface; the 2020s have seen QUIC-specific side channels and TLS-trace-based attacks against various deployments.
This module is the network-visible-leakage treatment. We'll cover the major side-channel families that affect encrypted network protocols, the mitigations available, and the residual leakage that even well-designed protocols accept. The scope is network-visible; cryptographic side-channel attacks against the cipher implementation itself (cache timing, branch prediction) belong to a different module.
Prerequisites
tls-1-3-handshake-byte-by-byte— for the TLS structure side channels exploit.tcp-at-the-wire-level— for understanding what's visible at the TCP layer beneath encryption.traffic-analysis-fundamentals— for the broader observable-feature framework.
Learning objectives
- Distinguish compression oracles, length-based side channels, timing side channels, and protocol-behavior side channels in encrypted protocols.
- Explain how attacks like CRIME and BREACH leveraged compression to recover plaintext from encrypted traffic.
- Describe TLS record length and QUIC packet structure as ongoing side-channel surfaces.
- Evaluate mitigation strategies and the residual leakage that survives them.
What's a side channel here
A side channel in this module is an information leak through observable behavior that wasn't intended as a communication path. The leakage isn't through the protocol's stated outputs (which are encrypted) but through metadata or structure the protocol exposes as a side effect of how it operates.
Categories:
Compression side channels. When data is compressed and encrypted, the compressed length depends on what was compressed. If an attacker can include attacker-controlled data alongside secret data in the same compression context, they can guess at secret bytes by observing compressed lengths.
Length-based side channels. Even without compression, the length of a message depends on its content. A short HTTP response and a long one are observably different sizes. TLS record padding helps but doesn't fully address this.
Timing side channels. The time to process a request can depend on the request's content. A login attempt with a wrong username vs. a wrong password might take different times because the system aborts at different points.
Protocol-behavior side channels. How a protocol responds to specific inputs (error vs. success, retry vs. give-up, fast vs. slow) can leak information about state.
Order side channels. The order in which a protocol does things — sends responses, processes requests, allocates resources — can encode state information.
Side channels are surprisingly powerful because they require zero cryptographic compromise. The cipher is doing its job; the structure around the cipher is doing the leakage.
Compression oracles: CRIME and BREACH
The classic compression-oracle attack pattern, as demonstrated by CRIME (2012) and BREACH (2013):
The setup:
- The victim's browser maintains an authentication cookie in HTTPS requests to a target site.
- The attacker can cause the victim's browser to make requests with attacker-chosen content (via JavaScript on a page the victim visits).
- The attacker observes the size of the resulting encrypted requests.
- The HTTPS request body and the cookie are compressed before encryption (HTTP compression for BREACH, TLS compression for CRIME).
The exploit:
- The attacker injects a guess at the cookie's first character: "Cookie: session_id=A".
- If the guess matches the actual cookie, the compressor finds redundancy between the injection and the actual cookie value, producing shorter compressed output.
- If the guess doesn't match, no redundancy is found, output is longer.
- The attacker observes the compressed (encrypted) length and concludes whether the guess was correct.
- Repeat for each character: "session_id=AA", "session_id=AB", ..., until the right character is found.
- Move to the second character: "session_id=Ax", "session_id=Ay", ...
- Recover the entire cookie character by character.
The attack works because compression operates over the whole request body (or TLS record), so attacker-controlled and secret data end up in the same compression dictionary. The information leaks through the dictionary's behavior on similar substrings.
Mitigations:
- Disable compression on sensitive data. TLS compression was deprecated in TLS 1.3 specifically because of CRIME. HTTP compression is harder — modern web apps depend on it for performance — but selective disabling for endpoints carrying authentication tokens helps.
- Random padding. Add unpredictable padding to the compressed output so the size is no longer deterministic. Helps but doesn't fully eliminate the side channel.
- Separate compression contexts. Don't compress secret data and attacker-controllable data in the same context. Architecturally clean but operationally hard.
- CSRF tokens. Adding random per-request data to responses makes the secret a moving target.
Modern TLS deployments don't use TLS-layer compression. HTTP-layer compression remains (gzip, Brotli) and remains a potential vector; production sites address it through a combination of CSRF tokens, length normalization, and careful endpoint design.
TLS record lengths
TLS records are encrypted-and-authenticated, but their lengths are observable at the TCP layer. Each record has a header containing its length; the length is a side channel revealing the application data size.
What lengths reveal:
- Resource sizes. Loading a page reveals approximate sizes of HTML, CSS, JS, images. Specific webpages have characteristic size distributions.
- Request types. A short request looks like an API call; a large request looks like a file upload.
- Compression effectiveness. A highly-redundant payload compresses better than random; the post-compression size leaks information about content patterns.
- Per-message identifiability. Specific WebSocket messages might have distinctive sizes (a chat message vs. a typing indicator vs. a presence update).
TLS 1.3 added record padding via the padding_length parameter; implementations can pad records to multiples of 256 or 1024 bytes (or whatever they choose) to reduce length-based leakage. The padding is included in the encrypted record so observers see the padded length, not the original.
Mitigations:
- Pad TLS records to coarse multiples of size (loses some efficiency but reduces length granularity).
- Aggregate small messages into single records (reduces per-message leakage).
- Use constant-rate transmission for the highest-stakes flows (impractical for normal browsing).
The residual: TLS record lengths are still a substantial side channel for most applications, even with padding. Going from "a 100-byte and a 500-byte distinction" to "a 1024-byte vs 2048-byte distinction" reduces but doesn't eliminate the signal.
QUIC and HTTP/3
QUIC encrypts more than TLS+TCP does. The QUIC initial handshake encrypts itself with derived keys (so even the connection setup is opaque to most observers), and QUIC has its own packet-numbering and framing inside the encrypted payload.
What QUIC still exposes:
- Initial packet sizes. The QUIC initial packet has a known minimum size (1200 bytes) for amplification protection. The size is observable.
- Packet sequence patterns. The number and timing of QUIC packets in a flow is observable; the relationship between packet count and HTTP/3 frame structure is somewhat predictable.
- Migration patterns. QUIC supports connection migration; observable migration events leak that the client changed networks.
- Error frames. QUIC peers send specific framing for errors; the presence of certain error frame types may be observable through size patterns.
Several papers (including "QUIC Network Traffic Classification Using Ensemble Machine Learning" and related work) have demonstrated that QUIC-specific traffic analysis works at substantial accuracy for many tasks. Encryption is more comprehensive than TLS-over-TCP, but flow structure, packet counts, and timing remain observable.
HTTP/2 and HTTP/3 side channels
HTTP/2 multiplexes multiple streams over one TLS connection. HTTP/3 does the same over QUIC. The multiplexing introduces side channels:
- Stream priority and dependencies. HTTP/2 supports priority frames; the priority structure can reveal request semantics.
- Frame boundaries. Even when payloads are encrypted, the stream the frame belongs to (via stream ID) and the frame size are observable. With careful analysis, individual responses can sometimes be distinguished.
- HEADERS vs. DATA frame ratios. A request-response with many small data frames is distinguishable from one with a few large ones.
- Push promise patterns. Server push (HTTP/2) or fast advertisement (HTTP/3) reveals which resources the server thinks the client will need.
The Mazurczyk et al. "Steganography of HTTP/2" paper demonstrated that even without exploiting the protocol, the HTTP/2 frame structure is rich enough to support both side-channel attacks and steganographic encoding.
Hands-on exercise
Toy compression-oracle pseudocode.
Tools: notes. Runtime: 15 minutes.
Sketch the compression-oracle attack:
target_endpoint = "https://victim.example.com/api"
cookie_header = "Cookie: session_token=SECRET_VALUE"
known_prefix = "Cookie: session_token="
candidate_chars = "abcdefghijklmnopqrstuvwxyz0123456789"
recovered_cookie = ""
for position in range(token_length):
sizes = {}
for ch in candidate_chars:
guess = recovered_cookie + ch
# Inject the guess into the request body so it shares a
# compression context with the actual cookie.
body = "guess=" + known_prefix + guess
request_size = make_request_observe_size(target_endpoint, body)
sizes[ch] = request_size
# The character producing the smallest compressed size is the correct one
best_char = min(sizes, key=sizes.get)
recovered_cookie += best_char
print(f"Recovered: {recovered_cookie}")
Now walk through what assumptions this requires:
- The attacker can cause the victim's browser to make requests with attacker-chosen body content. (Requires JavaScript context; cross-origin restrictions limit this in practice.)
- The cookie and the injected content share a compression context. (Requires HTTP compression to be enabled and the cookie to be in a header that gets compressed alongside the body — true for some legacy configurations.)
- The attacker can observe the size of the encrypted request. (Requires being on the network path between victim and server.)
The conditions are restrictive enough that BREACH-style attacks are rare in modern deployments, but the principle illustrates how compression side channels work.
Mitigation matrix.
| Side channel | Mitigation | Cost | Residual |
|---|---|---|---|
| Compression oracle | Disable compression on sensitive endpoints | Performance loss | Mostly addressed |
| TLS record length | Pad records to coarse multiples | Bandwidth overhead | Coarser-granularity leakage |
| Timing | Constant-time response handling | Code complexity | Partial; OS scheduling jitter |
| Compression effects | Random padding in compressed output | Bandwidth + slight CPU | Reduced but not eliminated |
| HTTP frame structure | Aggregate frames, pad to multiples | Latency, complexity | Stream-level observability |
| QUIC initial packet | Pad to standard minimum | Bandwidth (already done) | Initial flow visible |
The pattern: every mitigation costs something; complete elimination is generally impossible without dramatic changes (constant-rate transmission). Production protocols pick partial mitigations that address the highest-impact attacks.
Common misconceptions and traps
"TLS 1.3 fixed all side channels." It eliminated TLS-layer compression (addressing CRIME) and encrypted more handshake messages (addressing some metadata leakage). It did not eliminate length-based, timing-based, or HTTP-compression-based side channels.
"Side channels are theoretical." CRIME and BREACH were practical and worked against real deployments. Modern variants continue to be published. They're real, just often contextual.
"Padding to fixed sizes solves length leakage." It reduces granularity but doesn't eliminate it. An adversary can still distinguish "this connection sent 1024 bytes" from "this connection sent 5120 bytes" — coarser, but still informative.
"HTTP/3 eliminates TCP fingerprinting." It changes the layer (QUIC over UDP instead of TCP), but adds its own observable structure. Side channels migrate; they don't disappear.
"My compression isn't a problem because I'm not handling secrets." The attack requires attacker-controllable content shared with secret content in a compression context. If your application has authenticated state, an attacker who can include their content alongside that state may extract it.
Wrapping up
Side channels in encrypted protocols leak information through structure rather than content. Compression oracles, TLS record lengths, QUIC packet structure, HTTP/2 frame patterns — all leak observable signal that can identify, classify, or in extreme cases recover plaintext from encrypted traffic.
The mitigations are partial. Disabling compression addresses CRIME-style attacks; padding addresses length-based attacks at coarser granularity; constant-time handling addresses timing attacks at code-complexity cost. None eliminates all side channels; all involve trade-offs.
For protocol designers and operators, the lesson is structural awareness: every observable characteristic of an encrypted protocol is a potential side channel; choosing what to expose and what to mask is a design decision with real security implications. For users, the lesson is that "encrypted" doesn't mean "no information leaked"; the layers above the cipher matter as much as the cipher itself.
The next module (network-level-traffic-analysis — coming soon) covers what a network-level observer with multiple vantage points can do — flow correlation, RAPTOR-style routing attacks, NetFlow analysis. Side channels and network-level analysis often combine in operational attacks; this module's defenses become part of a broader threat picture there.
Further reading
- BREACH: Reviving the CRIME Attack — Prado et al., 2013 — the canonical compression-oracle attack on HTTP.
- Adaptive TLS-trace fingerprinting — how TLS records can be analyzed for application identification.
- QUIC traffic characterization papers — recent work on what QUIC's encrypted framing still exposes.
- Machine Learning Interpretability Meets TLS Fingerprinting — how structural features of TLS feed into ML.
// related reading
Decoy routing and refraction networking
Telex, TapDance, Slitheen, and Conjure: how cooperative infrastructure on ordinary network paths changes the evasion game.
Hysteria and QUIC-based transports
Why QUIC became an evasive substrate, how Hysteria uses it, and what QUIC-based camouflage still leaks to modern detectors.
Operational anonymity for engineers
Compartmentation, browser discipline, transport choice, telemetry minimization, and how to turn anonymity theory into a survivable daily operating model.