Internet Protocols · TLS

Traffic Analysis Against TLS: Record Lengths, Timing, and Padding

What ciphertext lengths reveal about plaintext, how padding is expressed in the record layer, and why uniform padding is not the same as hiding a request.

Encryption hides content and preserves structure. A TLS record’s length is not encrypted, because the receiver needs it to know how many bytes to read before it can decrypt anything. The consequence is that the size of every fragment is visible to anyone on the path, and the sequence of sizes over a connection describes the shape of the interaction even when the words are unreadable.

The length is a leak by construction

The five-byte record header contains a two-byte length field that counts ciphertext bytes. The receiver must read that many bytes before it can authenticate and decrypt, so the field cannot be encrypted under the same key as the payload it describes. A length-hiding construction would need to encrypt a length prefix and then read incrementally, which would make the receiver’s buffering dependent on unauthenticated data and would break the constant-time properties that the record layer relies on.

What an observer learns from a length is the plaintext length plus a small constant. For AES-GCM and ChaCha20-Poly1305 the overhead is the 16-byte tag, one byte for the inner content type, the padding bytes if any, and the five-byte header, as the record header layout sets out. A record whose length field says 1600 therefore carries 1578 bytes of application data if there is no padding, or fewer if there is. The mapping is exact, not statistical.

The value of that leak depends on what the application sends. A request whose size is determined by a small set of templates, such as a search query or a form submission, maps a length to a template with high confidence. A response whose size depends on the data retrieved reveals whether the result was empty, small, or large, and often reveals more, because the response is generated from a template that includes the data. A page that says “no results” and a page that says “one result” have different lengths, and the difference is visible without decryption.

Where padding can be applied, and what it costs

The TLS 1.3 inner plaintext is content || type || padding, where padding is a run of zero bytes. The receiver strips trailing zeros until it reaches a nonzero byte and interprets that byte as the content type. This means a record may be padded to any length up to the record size limit, and the padding is inside the authenticated envelope, so it cannot be removed or altered by an observer.

Padding costs bandwidth in direct proportion to the bytes added. It also costs a little computation, since the AEAD processes the padding as part of the plaintext. The interesting question is not whether padding can be added but what padding policy achieves a useful hiding property.

Uniform padding to a fixed record size removes the variation between records, but only if every record is padded to the maximum, which multiplies the traffic by the ratio of maximum to average. A 100-byte request padded to 16 KiB costs 163 times the bytes. A policy that pads to the nearest multiple of some block, such as 256 bytes, reduces the granularity of the leak without the full multiplier, and an observer then learns the length only to within 256 bytes. The choice is a point on a curve, not a binary.

There is a second ceiling that matters. The ciphertext record may not exceed 2^14 + 256 bytes, so padding cannot push a record beyond that limit; a sender that wants to pad a full-size fragment must split it. The limit is a compatibility measure, not a security one, and it constrains the padding policies that a stack can implement.

Timing carries what lengths do not

Record lengths describe how much was sent. Timing describes when. A connection that pauses after each request reveals that the interaction is turn-based, and the length of the pause is the server’s processing time plus the network delay. A server whose response time depends on whether a username exists, or on whether a record was found, produces a timing difference that is measurable if the difference is larger than the network jitter.

The record layer cannot hide this, and padding does not help. What helps is making the response time independent of the secret, by processing both branches in the same time or by adding a deliberate delay. That is an application concern, and the protocol’s contribution is only that the encrypted connection does not add its own variable delays: a correct implementation does not branch on decryption failure in a way that is observable, and the AEAD’s tag check is constant-time by design.

The interaction between timing and compression is a separate hazard. Removing compression from TLS, as the removals in TLS 1.3 describe, closed the length-based compression oracle at the record layer. Compression above the record layer, in an application protocol or in a content encoding, reintroduces exactly the same leak, because the compressed size still depends on how well the plaintext matches the attacker’s guess.

Padding whole messages rather than records

Record-level padding hides the size of a fragment. It does not hide the number of fragments, and it does not hide the total size of a message that spans several records. A response of 100 KiB sent as seven fragments of 16 KiB each reveals its total size through the count, and padding each fragment to the maximum changes nothing about the count. Hiding the total requires padding the message, which means the sender must know the message’s boundary and must be willing to add padding at the end that the receiver can distinguish from content.

TLS has no message-level padding because the record layer does not know about messages. An application protocol that wants to hide response sizes must define its own framing with a length field and a padding allowance, and the receiver must strip the padding after decrypting. That is a real design, used in protocols that care about size leaks, and it costs the same bandwidth multiplier as record padding plus the complexity of a second framing layer.

The practical asymmetry is that hiding a request is often more valuable than hiding a response. A request’s length identifies which page is being fetched; a response’s length identifies what was on it. Request padding is cheap because requests are small, and a policy that pads every request to a fixed size costs little in absolute terms. Response padding is expensive because responses are large, and a policy that pads them to a fixed size can multiply the traffic by an order of magnitude.

What an observer can reconstruct without breaking anything

Given lengths and timing, an observer can often identify the application protocol, the sequence of operations, and sometimes the specific operation, without any cryptographic weakness. A connection whose first records are a few hundred bytes followed by a large response is a page fetch; one with many small alternating records is an interactive session; one with a long steady stream in each direction is a file transfer. None of this requires the keys.

The mitigation space is correspondingly limited. Padding reduces length granularity at a bandwidth cost. Fixed-size requests remove the request-size signal at a small cost. Deliberate delays remove timing signals at a latency cost. None of them removes the fundamental signal that a connection exists, that it carries data, and that it lasts for some period of time, and each of them trades a measurable resource for a partial reduction in what is visible.

The record layer’s role in all of this is to be honest about what it protects. It protects contents and integrity, under keys whose per-record construction is sound and whose usage limits are enforced. It does not protect metadata, and no arrangement of padding within the protocol changes that. An application that needs stronger protection against traffic analysis must add it above the record layer, where the message boundaries are known and where a padding policy can be expressed in terms of the thing being hidden.

References

  1. RFC 8446: The Transport Layer Security (TLS) Protocol Version 1.3 — IETF
  2. RFC 5246: The Transport Layer Security (TLS) Protocol Version 1.2 — IETF
  3. RFC 6973: Privacy Considerations for Internet Protocols — IETF