Here is the entire glossary entry for “ISN” in RFC 793, September 1981:
“The Initial Sequence Number. The first sequence number used on a connection, (either ISS or IRS). Selected on a clock based procedure.”
That sentence is the origin of TCP sequence number attacks, and there is no mistake in it. The clock-based procedure was sound engineering: it solved the problem it was written to solve, it still solves that problem, and no line of it was ever wrong. What changed was not the specification. What changed was who could reach your host.

It is the most instructive security story in the stack precisely because there is no villain and no bug. A decision was correct under an assumption nobody wrote down, the assumption stopped holding, and nothing announced it. Then it happened again, to the same protocol, for an unrelated reason.
Selected on a Clock Based Procedure
RFC 793 states the goal of ISN selection plainly, and the goal is not secrecy:
“To avoid confusion we must prevent segments from one incarnation of a connection from being used while the same sequence numbers may still be present in the network from an earlier incarnation.”

The mechanism follows: a 32-bit counter “whose low order bit is incremented roughly every 4 microseconds. Thus, the ISN cycles approximately every 4.55 hours.” Since a segment was assumed not to outlive the Maximum Segment Lifetime, which is far shorter than 4.55 hours, RFC 793 concludes: “we can reasonably assume that ISN’s will be unique.”
Unique. That is the whole requirement, and the clock delivers it perfectly. Uniqueness against stale packets and unpredictability against an adversary are different properties, and in 1981 the specification asked for one of them. (The three-way handshake does not supply the other: it exists to tell a live connection attempt from a stale duplicate SYN, as RFC 793 says in those terms. It was never an authentication step.)
RFC 793 Does Have a Security Section. It Is About Filing Cabinets.
The temptation is to say the 1981 authors did not think about security. They did. RFC 793 has a section numbered 2.9, titled “Precedence and Security”:
“Not all TCP modules will necessarily function in a multilevel secure environment; some may be limited to unclassified use only, and others may operate at only one security level and compartment.”

“Security” and “compartment” appear dozens of times across the document. The word “attack” appears zero times. Neither does “adversary.” There is no Security Considerations section, because the RFC series did not require one yet.
That combination is the finding. The 1981 threat model is not absent — it is written down, and it is about classification labels among trusted parties: keeping material marked at one security level handled at that level, on a network you had to be institutionally authorised to attach to at all. The stranger forging packets from an address he does not own is missing because he was not, in any practical sense, on the network.
By 2022 the successor — RFC 9293, August 2022, STD 7 — uses “attack” 26 times and carries a “Security and Privacy Considerations” section. Same protocol, different planet.
Eleven Years From a Paper to an Advisory
The weakness was described in 1985, by Robert T. Morris, in “A Weakness in the 4.2BSD UNIX TCP/IP Software” (CSTR 117, AT&T Bell Laboratories) — the citation as it appears in both RFCs that eventually fixed it. Then, for roughly a decade, not much.
The hinge is the opening sentence of RFC 1948 (S. Bellovin, AT&T Research, May 1996):
“IP spoofing attacks based on sequence number spoofing have become a serious threat on the Internet (CERT Advisory CA-95:01).”
That parenthesis is the most important thing in the document. Bellovin is not arguing from first principles that an attack is possible; he is citing a numbered incident advisory as evidence it is already happening. RFC 6528 dates the shift the same way, noting the 1985 attack’s “exploitation was widely publicized about 10 years later.”
The mechanism, at the level a defender needs and no further: services of that era authenticated by source IP address alone — RFC 1948 names rlogin and rsh. If a server’s next initial sequence number could be predicted, an attacker who never received a reply could still produce the one value the server was waiting for. Address-based trust plus a predictable counter is the whole of it; the remaining steps are thirty years dead and reconstructing them would serve nobody.
One more line from RFC 1948, because the pattern recurs constantly:
“Instead, Berkeley-derived kernels increment it by a constant every second, and by another constant for each new connection.”
The specification was predictable in principle. What shipped was more predictable still, for ordinary performance reasons — and nobody noticed, because predictability was not on anyone’s list of things to avoid.
Why “Just Randomise It” Was the Wrong Fix
Both RFCs reject the obvious remedy, and the reason is the most useful engineering lesson here. RFC 1948: “simple randomization … will not work well.” RFC 6528 spells it out:
“Simple random selection of the TCP ISNs would mitigate those attacks that require an attacker to guess valid sequence numbers. However, it would also break the 4.4BSD ‘heuristics’…”

The 1981 property still has to hold. Stacks had come to rely on ISNs increasing monotonically across connections, in order to accept a new incarnation while the previous one was still in TIME-WAIT. Throw randomness at the field and you buy a security property by destroying an interoperability one. The fix had to add a guarantee without spending an existing one.
What RFC 1948 proposed, and RFC 6528 took to Standards Track in February 2012, keeps the clock and adds a per-connection secret. RFC 9293 carries it as normative text:
ISN = M + F(localip, localport, remoteip, remoteport, secretkey)
M is still the 4-microsecond timer; F() is a pseudorandom function of the connection’s own four-tuple plus a locally held secret. Every connection gets its own sequence-number space, with the 1981 behaviour intact inside each one.
The requirement is stronger than “hard to guess”
RFC 1948 called the key property “vital.” RFC 9293 makes it a normative keyword:
“F() MUST NOT be computable from the outside (MUST-9), or an attacker could still guess at sequence numbers from the ISN used for some other connection.”
It is not enough for an ISN to be unguessable in isolation. Observing the ISNs on connections you are entitled to see must tell you nothing about the ISNs on connections you are not. Half-measures fail that test publicly: five years after RFC 1948, CERT issued a second advisory — CA-2001-09, “Statistical Weaknesses in TCP/IP Initial Sequence Numbers,” cited in RFC 6528’s own references. Randomness is not something you can confirm by looking at the output.
The Lineage, Document by Document
| Document | Date | Status | What it changed |
|---|---|---|---|
| RFC 793 | Sept 1981 | Obsolete (by 9293) | ISN from a plain 32-bit clock. Goal stated as uniqueness. No threat model for a hostile peer. |
| CERT CA-95:01 | 1995 | Incident advisory | Cited in RFC 1948 as evidence the attack was real and in use, not theoretical. |
| RFC 1948 | May 1996 | Informational (obsoleted by 6528) | Proposes ISN = M + F(four-tuple, secret). Calls itself a “palliative measure.” |
| RFC 4953 | July 2007 | Informational | Surveys spoofing defences; quantifies how rising bandwidth erodes the margin. |
| RFC 5961 | Aug 2010 | Standards Track | Tightens in-window RST, SYN and data acceptance rules. |
| RFC 6528 | Feb 2012 | Standards Track | Takes the 1948 algorithm to Standards Track; formally updates RFC 793. |
| RFC 9293 | Aug 2022 | STD 7 | Folds 6528 in, updates 5961. F() becomes MUST-9. |
Twenty-six years from “vital” in an Informational memo to a numbered MUST in the internet standard. The engineering finished in 1996; the rest was deployment.
The Second Time It Aged, the Only Thing That Moved Was Bandwidth
RFC 5961 (Ramaiah, Stewart & Dalal, August 2010) says the quiet part out loud in its first paragraph:
“Yet, when it was standardized over 20 years ago, the Internet was a different place, lacking many of the threats that are now common.”

RFC 793 said a receiver should accept a RST anywhere inside the current receive window — a robustness rule, not a security rule. But it functioned as a security margin, and RFC 4953 (J. Touch, USC/ISI, July 2007) names it in exactly those terms: TCP was “indirectly protected” by the window check and by the obscurity of the endpoint and port numbers.
An unwritten margin cannot be monitored, and this one was being spent. RFC 5961 names the cause, which is not an attacker getting cleverer:
“With rises in bandwidth to both the home and office, it can only be expected that the values for default window sizes will continue to rise in order to better take advantage of the newly available bandwidth.”
Window scaling exists so that fast, long-distance links can actually be filled: a pure performance feature, adopted for pure performance reasons. (For the capacity everyone was chasing, our guide to measuring bandwidth covers the numbers.) RFC 4953 states the cost in one line: “The susceptibility to attack increases with the square of the bandwidth, and thus presents a significant vulnerability for recent high-speed networks.”
Nothing in TCP changed. Broadband happened, everyone opened their windows to use it, and a margin nobody had booked as an asset was spent. The worst-affected connections were long-lived and predictable — RFC 5961 singles out BGP sessions between known peers, where a reset costs real routing convergence. Its fix, folded into RFC 9293, is unglamorous: require an exact sequence match before honouring a RST, answer a suspicious SYN with a challenge ACK rather than acting on it, check the ACK value before accepting data. The receiver stops trusting “plausible” and starts requiring “exact.”
Two Dictionary Entries, Forty-One Years Apart
| Specification | Definition of “ISN” |
|---|---|
| RFC 793 (1981) | “The first sequence number used on a connection, (either ISS or IRS). Selected on a clock based procedure.“ |
| RFC 9293 (2022) | “The first sequence number used on a connection (either ISS or IRS). Selected in a way that is unique within a given period of time and is unpredictable to attackers.“ |
The 1981 entry describes a mechanism. The 2022 entry states two required properties — and one of them contains a word that appears nowhere in the 1981 document at all.
RFC 6528 does one further thing that is easy to skim past. Defending its continued use of MD5, it writes its own threat model into the standard and bounds it: the algorithm “simply aims at reducing the chances of an off-path attacker guessing the ISN of a new connection, and thus in our threat model it is not worth the effort for an attacker to try to learn the secret key.” That is a dated, falsifiable statement about when the design stops being adequate — the sentence RFC 793 had no vocabulary to write.
Both RFCs close on the same honesty, sixteen years apart and almost word for word. RFC 6528: “Good sequence numbers are not a replacement for cryptographic authentication … At best, they are a palliative measure.” RFC 1948 got there first, with a contraction where its successor has two words. The designers labelled their own fix a stopgap. Three decades on it is still what your stack does, because the real answer — authenticating the transport — was always the expensive one.
Where Your Own 1981 Decision Is Sitting
The transferable lesson is not “randomise your identifiers.” It is that a design decision is only as good as the threat model it was made under, and nothing in your system tells you when that model expires.

Both failures share an anatomy, and neither involves carelessness. A choice is correct given an assumption about who is present and what they can do. The assumption is never recorded beside the choice, because at the time it is not an assumption — it is just the world. Something unrelated then falsifies it: in 1995 the population changed; in the 2000s the bandwidth changed, which nobody would have listed as a security variable. And no alarm fires, because nothing broke.
Every system has one of these. A sequential primary key in a URL, chosen when the application was internal-only. A service reachable without authentication because it sits “on the internal network,” decided before the network flattened. An input field nobody sanitises because “nobody would ever paste that here” — the assumption that ages fastest of all, and the reason this story keeps repeating in protocols far younger than TCP, DNS very much included.
So: write the assumption beside the decision, with its expiry condition — not “we use a counter here” but “we use a counter here because only authenticated internal callers can observe it; if that stops being true, this is wrong.” And watch the inputs, not just the incidents. “Has anyone attacked this?” would have returned no for years while the RST margin evaporated underneath. The question that catches it is “has any input to the original calculation moved?” Bandwidth had moved by orders of magnitude; nothing else needed to.
There is a reason this story belongs to TCP rather than to some tidier protocol that lost. The hub this article sits in argues that TCP/IP won on adoption, deployment cost and timing rather than demonstrated merit — and winning at that scale is what dragged a 1981 document, written for a few hundred vetted hosts, in front of a threat model it was never designed to survive. Ubiquity converts a reasonable local decision into a global one you can no longer revise. The clock is still ticking in your kernel. It just has a secret bolted to it now.
❓ Frequently Asked Questions
Are TCP sequence number attacks still a practical risk in 2026? The original blind-spoofing attack against address-authenticated services is not, on any stack implementing RFC 6528’s ISN generation — effectively all of them, since RFC 9293 carries it as normative text, and the services that made it devastating are long gone. The residual risk belongs to an on-path observer, and both RFC 1948 and RFC 6528 say plainly that good sequence numbers were never a defence against that.
Why not simply make the initial sequence number fully random? It would break something that still works. Stacks derived from 4.4BSD use monotonically increasing ISNs to accept a new incarnation of a connection while the previous one is still in TIME-WAIT, and RFC 6528 states that simple randomisation “would also break the 4.4BSD ‘heuristics’.” The standardised fix keeps the clock and adds a per-connection keyed function, so the 1981 property survives alongside the new one.
What is the difference between the ISN weakness and the blind RST problem? Different documents, same lesson. The ISN weakness (RFC 1948, then RFC 6528) is about predicting the start of a connection you are not part of. The blind in-window problem (RFC 4953, then RFC 5961) is about injecting into one already running — and it worsened for a reason unrelated to attackers: receive windows grew to exploit rising bandwidth, and the search space shrank in proportion.
Is RFC 793 still the TCP specification? No. RFC 9293 obsoleted it in August 2022, is published as STD 7, folds in RFC 6528 and updates RFC 5961. Cite RFC 793 for the 1981 design narrative and RFC 9293 for what a conformant TCP must do today.
What fixes this class of problem rather than mitigating it? Authenticating the transport. Both RFC 1948 and RFC 6528 call good sequence numbers “at best … a palliative measure”; RFC 6528 names IPsec (RFC 4301) and the TCP Authentication Option (RFC 5925) as the real answers. Port randomisation (RFC 6056, BCP 156) helps by making the four-tuple harder to guess — but that is another layer of making things hard, not of making them verified.
📦 Recommended Resources
Some links below are Amazon affiliate links; buying through them supports this site at no extra cost to you.
Start with the free primary sources, because here they genuinely beat the books. Every document this article quotes is plain text, permanent, and one click away at rfc-editor.org. Read them in this order and you will have the whole story from the people who wrote it: RFC 793 (Sept 1981 — the clock, and §2.9’s very different idea of “security”), RFC 1948 (May 1996 — five pages, and the most quotable security memo in the series), RFC 6528 (Feb 2012 — the same fix, on Standards Track, with its threat model written down), and RFC 9293 (Aug 2022, STD 7 — what a conformant TCP must do today). Add RFC 5961 and RFC 4953 for the second half of the story, where bandwidth rather than an attacker is the thing that moved. Total cost: nothing. Total reading time: an afternoon.
The books below are for the part the RFCs assume you already have.
- Firewalls and Internet Security: Repelling the Wily Hacker by William R. Cheswick, Steven M. Bellovin and Aviel D. Rubin (Addison-Wesley Professional Computing Series) — Bellovin wrote RFC 1948, and this is the book-length version of the thinking behind it: what address-based trust actually assumes, and what happens to those assumptions at internet scale.
- Thinking Security: Stopping Next Year’s Hackers by Steven M. Bellovin (Addison-Wesley) — the direct companion to this article’s argument. Its subject is that security advice has a shelf life, and that most of what teams do is inherited from a threat model that has quietly expired.
- TCP/IP Illustrated, Volume 2: The Implementation by Gary R. Wright and W. Richard Stevens (Addison-Wesley) — the book both RFC 1948 and RFC 6528 cite by page number (p. 945) for the 4.4BSD TIME-WAIT heuristics that made “just randomise the ISN” the wrong answer. The source the standards themselves point at.
✅ Pro tip: Before buying anything, spend twenty minutes on a system you already own and write one sentence per security-relevant default: “this is safe because ___, and it stops being safe if ___.” The second blank is the one that matters. RFC 793 could not fill it in — the vocabulary did not exist in 1981. You have no such excuse.