flawopen.com/Incidents/Heartbleed

Heartbleed: one missing bounds check, half the internet

Critical — Memory disclosure CWE-125: Out-of-bounds Read Disclosed 7 April 2014
ELI5

You shout to a colleague across the office: "Say the word cat back to me — it's three letters long." They shout back "cat". Now you shout: "Say the word cat back to me — it's sixty thousand letters long." Instead of noticing that "cat" is only three letters, your colleague reads out "cat" followed by whatever else happens to be written on the desk around them — other people's passwords, private notes, everything.

Key terms on this page
TLS heartbeat
A keep-alive extension (RFC 6520) letting one side send a small payload the other must echo back, to confirm the connection is still live.
buffer over-read
Reading past the end of an allocated region. Unlike an overflow, it does not corrupt anything — it simply returns adjacent memory contents to the attacker.
forward secrecy
A property where compromising a server's long-term private key does not retroactively decrypt past recorded sessions. Its absence made Heartbleed's key leakage far worse.

What happened

In April 2014, researchers at Google and Codenomicon independently found a flaw in OpenSSL's implementation of the TLS heartbeat extension. CVE-2014-0160 allowed any client to read up to 64 kilobytes of the server process's memory per request, without authentication, repeatedly, and — critically — without leaving any trace in the server's logs.

OpenSSL underpinned a very large share of HTTPS on the public internet at the time, so the exposure was close to universal. What leaked was whatever happened to be in the process heap: session cookies, usernames and passwords in flight, decrypted request bodies, and in some cases the server's TLS private key itself.

The flaw had been present in OpenSSL since version 1.0.1 (March 2012) — roughly two years of exposure before anyone noticed.

The technical root cause

A heartbeat request carries a payload and a field stating that payload's length. The server was required to echo the payload back. OpenSSL allocated a response buffer using the attacker-supplied length and copied that many bytes out of the request — without ever checking that the request actually contained that much data.

The trusted-length antipattern

The length field and the data it describes both came from the attacker, but only the length was believed. When a request claimed 65535 bytes while carrying 3, the copy read 3 bytes of payload and then 65532 bytes of whatever sat next to it in the heap — and dutifully sent all of it back.

VULNERABLE (SIMPLIFIED)
/* payload length comes from the
   attacker's request */
n2s(p, payload);
pl = p;

buffer = OPENSSL_malloc(1 + 2 + payload
                        + padding);
bp = buffer;

/* copies `payload` bytes regardless of
   how much data actually arrived */
memcpy(bp, pl, payload);
FIXED
/* reject heartbeats whose claimed
   length exceeds the record we
   actually received */
if (1 + 2 + 16 > s->s3->rrec.length)
    return 0;  /* silently discard */

n2s(p, payload);
if (1 + 2 + payload + 16 > s->s3->rrec.length)
    return 0;  /* silently discard */

pl = p;

The fix is two comparisons. That ratio — a two-line check against a two-year, internet-wide exposure — is the reason Heartbleed became the standard illustration of how little code stands between working software and catastrophic failure.

Why remediation was so painful

Patching OpenSSL was the easy part. The hard part followed from a property of the bug: because it was silent and untraceable, no operator could prove they had not been exploited. With no logs to examine, the only defensible assumption was that every secret resident in the process memory during the exposure window had leaked.

That meant regenerating private keys, reissuing and revoking certificates at a scale the certificate authority ecosystem had never handled at once, invalidating every session, and prompting global password resets. Deployments without forward secrecy were worse off again: an attacker who had recorded encrypted traffic earlier could decrypt it retroactively once they held the key.

The lessons that actually transfer

FAQ

Why is it called an over-read rather than an overflow?

An overflow writes past a boundary, corrupting adjacent memory and often enabling code execution. An over-read only reads past it. Heartbleed never corrupted anything — it simply handed the attacker memory contents, which in this case was more than sufficient.

Could the private key really be recovered?

Yes. It was not guaranteed on any given request, but private key material can reside in the same heap. Repeated requests sampling different memory made recovery practical, and it was demonstrated publicly.

Related reading

Sources