Transport Layer Security (TLS) and Datagram Transport Layer Security (DTLS) Heartbeat Extension
updated by
- rfc-8447 — IANA Registry Updates for TLS and DTLS
Extracted elements (27)
HeartbeatRequest messages for liveness checking also serve to refresh NAT state for all intermediate NATs, solving the common problem of NAT state expiry for long-lived but idle TLS/DTLS connections.
HeartbeatRequest messages with padding serve as PMTU probe packets for DTLS, following the packetization-layer PMTU discovery method of RFC 4821. This avoids the need for a separate probe message type that would complicate the DTLS record layer.
Restricting the number of in-flight HeartbeatRequests to one at a time, combined with the DTLS retransmission scheme, ensures that heartbeats do not cause congestion problems when running over UDP or other transport protocols that lack their own congestion control.
The Heartbeat Extension was introduced because the only DTLS mechanism to detect peer liveness was a costly renegotiation, especially with unidirectional traffic. For TLS over TCP there was no standard keep-alive below the application layer. The extension provides a lightweight alternative without triggering renegotiation.
The Heartbeat Extension applies to TLS and DTLS and their adaptations over SCTP (RFC 3436, RFC 6083) and DCCP (RFC 5238). The retransmission behavior differs between reliable transports (send once; transport retransmits) and unreliable transports (application-level retransmit using DTLS flight timers).
HeartbeatRequest messages MUST NOT be sent to a peer that has indicated peer_not_allowed_to_send. If such an endpoint receives a HeartbeatRequest, it SHOULD drop the message silently and MAY send an unexpected_message Alert.
HeartbeatRequest messages SHOULD only be sent after an idle period that is at least multiple round-trip times long. This idle period SHOULD be configurable between one second and multiple minutes, with a tunable per-peer default.
If a handshake is initiated while a HeartbeatRequest is still in flight, the sending peer MUST stop the DTLS retransmission timer for that HeartbeatRequest. For DTLS, HeartbeatRequest messages from older epochs SHOULD be discarded.
If a received HeartbeatResponse message does not contain the expected payload, the message MUST be discarded silently. If it does contain the expected payload, the retransmission timer MUST be stopped.
If the payload_length of a received HeartbeatMessage is too large (i.e., it would exceed the actual message bounds), the received HeartbeatMessage MUST be discarded silently. This guards against out-of-bounds memory reads in implementations.
The sender of a HeartbeatMessage MUST use a random padding of at least 16 bytes. The padding of a received HeartbeatMessage MUST be ignored by the receiver.
The total length of a HeartbeatMessage MUST NOT exceed 2^14 bytes, or max_fragment_length when that extension has been negotiated per RFC 6066.
There MUST NOT be more than one HeartbeatRequest message in flight at a time. A message is considered in flight until the corresponding HeartbeatResponse is received or the retransmit timer expires.
Upon reception of an unknown HeartbeatMode value, an error Alert message using illegal_parameter as its AlertDescription MUST be sent in response.
When a HeartbeatRequest is received and a response is not prohibited, the receiver MUST send a corresponding HeartbeatResponse message carrying an exact copy of the payload of the received HeartbeatRequest.
When using reliable transport (SCTP or TCP), HeartbeatRequest messages only need to be sent once; the transport layer handles retransmissions. If no corresponding HeartbeatResponse is received after some time, the TLS/DTLS connection MAY be terminated by the initiating application.
When using unreliable transport protocols (DCCP or UDP), HeartbeatRequest messages MUST be retransmitted using the same simple timeout and retransmission scheme DTLS uses for flights (RFC 6347 Section 4.2.4). After repeated retransmissions without a valid HeartbeatResponse, the DTLS connection SHOULD be terminated, using the same threshold as for DTLS handshake messages.
Whenever a HeartbeatRequest message is received, it SHOULD be answered with a corresponding HeartbeatResponse message. HeartbeatRequest messages SHOULD NOT be sent during handshakes, and a receiving peer SHOULD discard them silently if they arrive during a handshake.
The Heartbeat Hello Extension negotiates support and willingness to receive heartbeats. A peer indicates either peer_allowed_to_send(1) — willing to receive HeartbeatRequest messages and respond — or peer_not_allowed_to_send(2), which only permits sending requests. The mode may change at each renegotiation.
The Heartbeat protocol runs on top of the TLS/DTLS Record Layer and defines two message types: heartbeat_request(1) and heartbeat_response(2), expressed in a HeartbeatMessageType enum with value 255 reserved.
IANA assigned the heartbeat content type value 24 in the TLS ContentType Registry (RFC 5246). This is the record-layer content type used to carry HeartbeatMessage records.
IANA assigned the heartbeat extension type value 15 in the TLS ExtensionType Values registry (RFC 5246), used to carry the HeartbeatExtension in ClientHello and ServerHello.
IANA created the Heartbeat Message Types registry (values 0–255, Expert Review). Initial assignments: heartbeat_request(1) and heartbeat_response(2). Values 0 and 255 are reserved.
IANA created the Heartbeat Modes registry (values 0–255, Expert Review). Initial assignments: peer_allowed_to_send(1) and peer_not_allowed_to_send(2). Values 0 and 255 are reserved.
The document states that the security considerations of RFC 5246 (TLS 1.2) and RFC 6347 (DTLS 1.2) apply in full, and that this extension introduces no new security considerations. In practice, the requirement to discard messages whose payload_length exceeds the actual received data (Section 4) is the critical guard against memory disclosure; failure to enforce it produced CVE-2014-0160 (Heartbleed).
HeartbeatMessage consists of: type (HeartbeatMessageType, 1 byte), payload_length (uint16, 2 bytes), payload (opaque, payload_length bytes), and padding (opaque, padding_length bytes). The padding_length equals TLSPlaintext.length (or DTLSPlaintext.length) minus payload_length minus 3, and MUST be at least 16 bytes.
The HeartbeatExtension Hello Extension carries a single HeartbeatMode field (1 byte). The HeartbeatMode enum values are peer_allowed_to_send(1) and peer_not_allowed_to_send(2), with value 255 reserved.