RFC 6056
Recommendations for Transport-Protocol Port Randomization
Internet Engineering Task Force (IETF) ๐ RFC 6056: Recommendations for Transport-Protocol Port Randomization โจ
Okay bestie, let's get into this! ๐ This one is a Best Current Practice (BCP 156), so it's the community's official "here's how to do it well" doc, not a strict must-do-or-else spec. It was written by M. Larsen (Tieto) and F. Gont (UTN/FRH) and published in January 2011. It's on the ISSN 2070-1721 track ๐ธ. It doesn't obsolete or update any other RFCs, so it's a fresh, standalone vibe ๐ง.
Part 1 covers the abstract, the boring-but-important legal and status stuff, the table of contents, and the intro. Let's go! ๐ฆ
Abstract ๐
Okay so, in the last few years, people started noticing a bunch of "blind" attacks that can hit TCP and similar protocols. ๐ญ These attacks can slow your throughput down, break your connections entirely, or even corrupt your data. Yikes! ๐
These attacks depend on the attacker being able to guess or know the five-tuple (Protocol, Source Address, Destination Address, Source Port, Destination Port) that identifies the exact transport-protocol instance being attacked. ๐ฅ
So this doc describes some simple, efficient ways for a client to pick its port number, so the attacker has a much harder time guessing the exact value ๐ฏ. It's not a replacement for crypto protection, to be clear ๐ซถ, but these port selection algorithms give you better security with basically no effort and no key management headaches ๐ . They're local policies you can roll out gradually, and they don't break the specs of the transport protocols that can use them: TCP, UDP, UDP-lite, SCTP, DCCP, and RTP (as long as the RTP app explicitly signals the RTP and RTCP port numbers) โจ.
Status of This Memo ๐
This memo documents an Internet Best Current Practice ๐.
This is an IETF product, so it reflects the consensus of the IETF community. It got public review and was approved for publication by the Internet Engineering Steering Group (IESG). If you want more on BCPs, check Section 2 of RFC 5741 ๐.
For the current status of this doc, any errata, and how to give feedback, go to http://www.rfc-editor.org/info/rfc6056 ๐.
Copyright Notice โ๏ธ
Copyright (c) 2011 IETF Trust and the persons identified as the document authors. All rights reserved. ๐
This document is subject to BCP 78 and the IETF Trust's Legal Provisions Relating to IETF Documents (http://trustee.ietf.org/license-info) as they were in effect on the publication date. Please actually read these, because they spell out your rights and restrictions ๐. Any Code Components pulled out of this document must include the Simplified BSD License text described in Section 4.e of the Trust Legal Provisions, and they come with no warranty, per the Simplified BSD License ๐.
This document may include material from IETF Documents or IETF Contributions published or made publicly available before November 10, 2008. The people who control the copyright in some of that material may not have given the IETF Trust permission to allow modifications outside the IETF Standards Process. So unless you get an adequate license from those copyright holders, this document may not be modified outside the IETF Standards Process, and derivative works may not be created outside it. The exception is formatting it for RFC publication or translating it into languages other than English ๐ธ.
Table of Contents ๐บ๏ธ
1. Introduction . . . . . . . . . . . . . . . . . . . . . . . . . 4
2. Ephemeral Ports . . . . . . . . . . . . . . . . . . . . . . . 5
2.1. Traditional Ephemeral Port Range . . . . . . . . . . . . . 5
2.2. Ephemeral Port Selection . . . . . . . . . . . . . . . . . 6
2.3. Collision of instance-ids . . . . . . . . . . . . . . . . 7
3. Obfuscating the Ephemeral Port Selection . . . . . . . . . . . 8
3.1. Characteristics of a Good Algorithm for the
Obfuscation of the Ephemeral Port Selection . . . . . . . 8
3.2. Ephemeral Port Number Range . . . . . . . . . . . . . . . 10
3.3. Algorithms for the Obfuscation of the Ephemeral Port
Selection . . . . . . . . . . . . . . . . . . . . . . . . 11
3.3.1. Algorithm 1: Simple Port Randomization Algorithm . . . 11
3.3.2. Algorithm 2: Another Simple Port Randomization
Algorithm . . . . . . . . . . . . . . . . . . . . . . 13
3.3.3. Algorithm 3: Simple Hash-Based Port Selection
Algorithm . . . . . . . . . . . . . . . . . . . . . . 14
3.3.4. Algorithm 4: Double-Hash Port Selection Algorithm . . 16
3.3.5. Algorithm 5: Random-Increments Port Selection
Algorithm . . . . . . . . . . . . . . . . . . . . . . 18
3.4. Secret-Key Considerations for Hash-Based Port
Selection Algorithms . . . . . . . . . . . . . . . . . . . 19
3.5. Choosing an Ephemeral Port Selection Algorithm . . . . . . 20
4. Interaction with Network Address Port Translation (NAPT) . . . 22
5. Security Considerations . . . . . . . . . . . . . . . . . . . 23
6. Acknowledgements . . . . . . . . . . . . . . . . . . . . . . . 24
7. References . . . . . . . . . . . . . . . . . . . . . . . . . . 24
7.1. Normative References . . . . . . . . . . . . . . . . . . . 24
7.2. Informative References . . . . . . . . . . . . . . . . . . 25
Appendix A. Survey of the Algorithms in Use by Some Popular
Implementations . . . . . . . . . . . . . . . . . . . 28
A.1. FreeBSD . . . . . . . . . . . . . . . . . . . . . . . . . 28
A.2. Linux . . . . . . . . . . . . . . . . . . . . . . . . . . 28
A.3. NetBSD . . . . . . . . . . . . . . . . . . . . . . . . . . 28
A.4. OpenBSD . . . . . . . . . . . . . . . . . . . . . . . . . 28
A.5. OpenSolaris . . . . . . . . . . . . . . . . . . . . . . . 28
1. Introduction ๐ท
Okay, so recently people have been noticing a bunch of "blind" attacks (meaning attacks that work without sniffing the packets of the transport-protocol instance being targeted) that can hit TCP [RFC0793] and similar protocols ๐ค. The fallout ranges from throughput reduction to broken connections or data corruption [RFC5927] [RFC4953] [Watson] ๐.
All of these attacks rely on the attacker guessing or knowing the five-tuple (Protocol, Source Address, Source Port, Destination Address, Destination Port) that identifies the transport-protocol instance being attacked ๐ฏ.
Services usually live at fixed, "well-known" ports [IANA] on the server host ๐ . Client apps connecting to one of these services contact the server by specifying its IP address and service port number. The client's own IP address and port are normally left unspecified by the app, so the client's networking stack picks them automatically ๐ง. Ports picked automatically by the stack are called ephemeral ports [Stevens] ๐ฆ.
So the server IP address, the well-known port, and the client IP address may all be known to an attacker, but the client's ephemeral port is usually unknown and has to be guessed ๐ต๏ธโโ๏ธ.
This document describes a bunch of algorithms for picking ephemeral port numbers, so an off-path attacker is less likely to guess the exact value ๐. These aren't a replacement for crypto protection like IPsec [RFC4301], the TCP MD5 signature option [RFC2385], or the TCP Authentication Option [RFC5925] ๐. For example, they don't help at all when the attacker can sniff the packets of the instance being attacked. Still, the algorithms give you better resistance to off-path attacks with very little effort and zero key management overhead โจ.
The mechanisms here are local modifications that can be deployed gradually, and they don't violate the specs of any of the transport protocols that might use them: TCP [RFC0793], UDP [RFC0768], SCTP [RFC4960], DCCP [RFC4340], UDP-lite [RFC3828], and RTP [RFC3550] (as long as the RTP app explicitly signals the RTP and RTCP port numbers with, e.g., [RFC3605]) ๐.
Since these are obfuscation techniques, the focus is on finding a reasonable balance between how much obfuscation you get and how easy the thing is to implement ๐ฐ. So the algorithms must be computationally efficient and must not require substantial state ๐ .
Quick note ๐: the technique of defending against "blind" attacks by obfuscating ephemeral port selection is commonly called "port randomization". But the goal of the algorithms in this document is to reduce the chances of an attacker guessing the ephemeral ports chosen for new transport-protocol instances. It is not about actually producing mathematically random sequences of ports ๐.
Throughout this doc, "transport-protocol instance" is the general term for an instance of a transport protocol (like a "connection" for connection-oriented protocols). "instance-id" is the short handle for the group of values that identify a transport-protocol instance. For TCP, that's the five-tuple {Protocol, IP Source Address, TCP Source Port, IP Destination Address, TCP Destination Port} ๐ซง.
The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in RFC 2119 [RFC2119] ๐.
Part 1 of 6. More parts coming, so stay tuned, bestie! ๐
2.2. Ephemeral Port Selection โจ
okay so every communication instance is identified by a five-tuple: {protocol, local IP address, local port, remote IP address, remote port} ๐ which means the ephemeral port picks have to result in a unique five-tuple every single time. that's the whole main character goal ๐
some implementations handle this with a per-protocol global "next_ephemeral" variable that equals the previously chosen ephemeral port + 1. it's giving simple and classic, so here's how the selection goes ๐
/* Initialization at system boot time. Could be random */
next_ephemeral = min_ephemeral;
/* Ephemeral port selection function */
count = max_ephemeral - min_ephemeral + 1;
do {
port = next_ephemeral;
if (next_ephemeral == max_ephemeral) {
next_ephemeral = min_ephemeral;
} else {
next_ephemeral++;
}
if (check_suitable_port(port))
return port;
count--;
} while (count > 0);
return ERROR;
Traditional BSD Port Selection Algorithm ๐ธ
Note: check_suitable_port() is a function that checks whether the resulting port number is acceptable as an ephemeral port. Basically it checks whether the resulting port number is unique, and it may also check that the port number isn't in use for a connection in the LISTEN or CLOSED states, and that it isn't in the list of port numbers that should not be allocated as ephemeral ports. In BSD-derived systems, check_suitable_port() would correspond to the in_pcblookup_local() function, where all the necessary checks would be performed. ๐ง
this algorithm works adequately as long as the number of transport-protocol instances (for each transport protocol) that have a lifetime longer than it takes to exhaust the total ephemeral port range is small, so that collisions of instance-ids are rare ๐
BUT (and this is a big but ๐) this method has a drawback. the "next_ephemeral" variable, and therefore the ephemeral port range, is shared between ALL transport-protocol instances, and the next ports chosen by the client are easy to predict ๐ญ if an attacker runs an "innocent" server that the client connects to, it's super easy for them to get a reference point for the current value of the "next_ephemeral" variable. it gets worse too: if an attacker could force a client to periodically establish, e.g., a new TCP connection to an attacker-controlled machine (or through an attacker-observable path), the attacker could subtract consecutive source port values to work out the number of outgoing TCP connections established globally by the target host within that time period. (up to wrap-around issues and instance-id collisions, of course ๐)
2.3. Collision of instance-ids ๐
so while the ephemeral port selection algorithm can check that the selected port number gives an instance-id that isn't currently in use by that system, the resulting five-tuple might STILL be in use at a remote system ๐ค for example, picture a client making a TCP connection with a remote web server, where the web server does the active close on the connection. the state info for this connection disappears on the client side (the connection gets moved to the fictional CLOSED state), but the instance-id stays in the TIME-WAIT state at the web server for 2*MSL (Maximum Segment Lifetime) โณ if that same client tries to make a new incarnation of the previous connection (a connection with the same instance-id as the one sitting in TIME_WAIT at the server), an instance-id "collision" happens. the effects of these collisions range from connection-establishment failures to TIME-WAIT state assassination (with the potential of data corruption) [RFC1337]. ๐ when a specific client keeps establishing TCP connections with a specific service at a server, these problems become super evident. so an ephemeral port selection algorithm should ideally minimize the rate of instance-id collisions ๐ซถ
a simple way to minimize the rate of these collisions would be to choose port numbers incrementally, so a given port number isn't reused until the rest of the port numbers in the ephemeral port range have been used for a transport protocol instance. HOWEVER, if a single global variable tracked the last ephemeral port selected, the ephemeral port numbers would be trivially predictable ๐ฌ which makes it way easier for an off-path attacker to "guess" the instance-id in use by a target transport-protocol instance. Sections 3.3.3 and 3.3.4 describe algorithms that select port numbers incrementally while still making it difficult for an off-path attacker to predict the ephemeral ports used for future transport-protocol instances ๐
a simple but inefficient approach to minimize the rate of instance-id collisions would be, e.g., for TCP, having both endpoints of a TCP connection keep state about recent connections (e.g., have both endpoints end up in the TIME-WAIT state). ๐ข
3. Obfuscating the Ephemeral Port Selection ๐ฆ
3.1. Characteristics of a Good Algorithm for the Obfuscation of the Ephemeral Port Selection ๐
there are several factors to think about when designing an algorithm for selecting ephemeral ports, including:
- Minimizing the predictability of the ephemeral port numbers used for future transport-protocol instances.
- Minimizing collisions of instance-ids.
- Avoiding conflict with applications that depend on the use of specific port numbers.
given the goal of improving the transport protocol's resistance to attack by obfuscating the instance-id selection, it's key to minimize how predictable the ephemeral ports are that get selected for new transport-protocol instances โจ the obvious move would be to just pick a random value within the chosen port number range, but this straightforward policy may lead to instance-id collisions, which can cause the interoperability problems (e.g., delays in establishing new connections, failures in connection establishment, or data corruption) we talked about in Section 2.3. ๐ฉ as mentioned in Section 1, the technique of mitigating "blind" attacks by obfuscating the ephemeral port selection is well-known as "port randomization." BUT the goal of the algorithms in this document is to reduce the chances that an attacker will guess the ephemeral ports selected for new transport-protocol instances, NOT to actually produce mathematically random ephemeral port numbers ๐ฒ (so no, it's not the same thing, bestie ๐ โโ๏ธ)
it's also worth noting that, provided adequate algorithms are in use, the larger the range ephemeral ports are selected from, the smaller the attacker's chances are of guessing the selected port number ๐
in scenarios where a specific client establishes transport-protocol instances with a specific service at a server, the problems described in Section 2.3 become evident. a good algorithm for minimizing instance-id collisions would consider the time a given five-tuple was last used, and would avoid reusing the least recently used five-tuples ๐ฐ๏ธ a simple approach to minimize the rate of collisions would be to choose port numbers incrementally, so a given port number isn't reused until the rest of the port numbers in the ephemeral port range have been used for a transport-protocol instance. however, if a single global variable were used to track the last ephemeral port selected, the ephemeral port numbers would be trivially predictable. ๐
it's important to note that a bunch of applications rely on binding specific port numbers that might fall inside the ephemeral port range. if one of those applications ran while its port number was in use, the application would fail ๐ฑ so ephemeral port selection algorithms avoid using those port numbers. ๐
port numbers that are currently in use by a TCP in the LISTEN state should not be allowed for use as ephemeral ports. if this rule isn't followed, an attacker could potentially "steal" an incoming connection to a local server application in at least two different ways ๐จ
first, an attacker could send a connection request to the victim client at roughly the same time the client tries to connect to the victim server application [CPNI-TCP] [TCP-SEC]. if the SYN segment for the attacker's connection request and the SYN segment for the victim client "cross each other in the network," and the attacker can know or guess the ephemeral port the client used, a TCP "simultaneous open" scenario happens. the incoming connection request from the client then gets matched with the attacker's socket instead of the victim server application's socket ๐
second, an attacker could specify a more specific socket than the "victim" socket (e.g., by specifying both the local IP address and the local TCP port). that way, incoming SYN segments matching the attacker's socket get delivered to the attacker, not the "victim" socket (see Section 10.1 of [CPNI-TCP]). ๐ต๏ธโโ๏ธ
it should be noted that most applications built on popular implementations of the TCP API (such as the Sockets API) do "passive opens" in three steps. ๐ช first, the application gets a file descriptor for inter-process communication (e.g., by issuing a socket() call). second, the application binds the file descriptor to a local TCP port number (e.g., by issuing a bind() call), which creates a TCP in the fictional CLOSED state. third, that TCP is put in the LISTEN state (e.g., by issuing a listen() call).
so with this kind of TCP API implementation, even if port numbers in use by TCPs in the LISTEN state weren't allowed as ephemeral ports, there's a window of time between the second and third steps where an attacker could be allowed to select a port number that would later be used for listening to incoming connections ๐ต therefore, these TCP API implementations should enforce a stricter requirement for allocating port numbers: port numbers that are in use by a TCP in the LISTEN or CLOSED states should not be allowed for allocation as ephemeral ports [CPNI-TCP] [TCP-SEC]. ๐
the aforementioned issue doesn't affect SCTP, since most SCTP implementations don't let a socket be bound to the same port number unless a specific socket option (SCTP_REUSE_PORT) is issued on the socket (i.e., this behavior has to be explicitly allowed beforehand) ๐ an example of a typical SCTP socket API can be found in [SCTP-SOCKET]. ๐
DCCP isn't affected by the exploitation of "simultaneous opens" to "steal" incoming connections, because the server and client state machines are different [RFC4340]. ๐โโ๏ธ however, it may be affected by the vector involving binding a more specific socket. so those tuples {local IP address, local port, Service Code} that are in use by a local socket should not be allowed for allocation as ephemeral ports. ๐ซง
3.2. Ephemeral Port Number Range โจ
okay so, as we talked about in Section 2.1, the dynamic ports are the range 49152-65535 ๐ but ephemeral port selection algorithms should use the whole range 1024-65535 ๐ that's the full glow-up, bestie.
this range includes the IANA Registered Ports ๐ธ so some of those port numbers might be needed to provide a particular service on the local host, and that could cause the problems we covered in Section 3.1 ๐ญ which is why port numbers that might be needed for providing a particular service at the local host SHOULD NOT be included in the pool of port numbers available for ephemeral port randomization ๐ if the host doesn't provide a particular service, though, the port can be safely given to ordinary processes ๐
a possible workaround is to keep a local list of port numbers that should not be allocated as ephemeral ports ๐ before allocating a port number, the ephemeral port selection function checks this list, so it skips ports that specific applications might need ๐ฆ but don't just exclude every registered port, lol, that's lazy ๐ administrators should figure out which services the local host may offer and SHOULD exclude only the corresponding registered ports ๐ซถ
ephemeral port selection algorithms SHOULD use the largest possible port range, since this reduces the chances of an off-path attacker guessing the selected port numbers ๐ฅ big range, big protection, periodt ๐
3.3. Algorithms for the Obfuscation of the Ephemeral Port Selection ๐ง
ephemeral port selection algorithms SHOULD obfuscate the selection of their ephemeral ports ๐ต๏ธโโ๏ธ this helps mitigate a bunch of attacks that depend on the attacker being able to guess or know the five-tuple that identifies the transport-protocol instance being attacked ๐ฑ main character energy is hiding your port, no cap โจ
the following subsections describe a few algorithms that could be implemented to obfuscate the selection of ephemeral port numbers ๐
3.3.1. Algorithm 1: Simple Port Randomization Algorithm ๐ฒ
okay so a bunch of systems have implemented simple ephemeral port number randomization to deal with the security issues from Sections 1 and 2.2 ๐ it looks like this:
/* Ephemeral port selection function */
num_ephemeral = max_ephemeral - min_ephemeral + 1;
next_ephemeral = min_ephemeral + (random() % num_ephemeral);
count = num_ephemeral;
do {
if(check_suitable_port(port))
return next_ephemeral;
if (next_ephemeral == max_ephemeral) {
next_ephemeral = min_ephemeral;
} else {
next_ephemeral++;
}
count--;
} while (count > 0);
return ERROR;
Algorithm 1
note: random() is a function that returns a 32-bit pseudo-random unsigned integer number ๐ง the output needs to be unpredictable, and typical implementations of the POSIX random() function don't necessarily meet that requirement ๐ โโ๏ธ see [RFC4086] for randomness requirements for security ๐
all the variables (in this and all the algorithms in this document) are unsigned integers ๐ข
so the initially chosen port might already be in use with the same IP addresses and server port that the socket needs ๐ฌ which means the resulting five-tuple might not be unique ๐ so multiple ports may have to be tried and checked against all existing transport-protocol instances before a port can be chosen ๐
web proxy servers, Network Address Port Translators (NAPTs) [RFC2663], and other middleboxes squish multiple peers into the same port space ๐ซ that means more used ephemeral ports, and so more chances of instance-id collisions โ ๏ธ but [Allman] has shown that, at least in the network scenarios used to measure collision properties of the algorithms in this document, the collision rate from those middleboxes is still very low ๐ฅนโจ
since this algorithm picks ports without looking at the ports it picked before, it can reuse port numbers too quickly, which could lead to instance-id collisions ๐ even if the port selection algorithm checks that an instance-id is unique, it might still be in use at the remote system ๐ก then a connection request could fail ([Silbersack] describes this problem for the TCP case) ๐ค
but here's the tea ๐ต this algorithm is biased towards the first available port after a run of unavailable port numbers ๐ if the local list of registered port numbers that shouldn't be allocated as ephemeral ports (from Section 3.2) is big, an attacker may have a much better shot at guessing a port number ๐จ
on the bright side, this algorithm picks ephemeral port numbers randomly, so it lowers the chances that an attacker guesses the ephemeral port for a target transport-protocol instance ๐ก๏ธ it also stops attackers from learning how many outgoing transport-protocol instances (e.g., TCP connections) the client has set up in some period of time ๐โจ
3.3.2. Algorithm 2: Another Simple Port Randomization Algorithm ๐
the pseudo-code below shows another algorithm for picking a random port number, where if a local instance-id collision is detected, another port number gets picked randomly ๐ฐ
/* Ephemeral port selection function */
num_ephemeral = max_ephemeral - min_ephemeral + 1;
next_ephemeral = min_ephemeral + (random() % num_ephemeral);
count = num_ephemeral;
do {
if(check_suitable_port(port))
return next_ephemeral;
next_ephemeral = min_ephemeral + (random() % num_ephemeral);
count--;
} while (count > 0);
return ERROR;
Algorithm 2
when there are a ton of port numbers already in use for the same destination endpoint, this algorithm might not be able to pick an ephemeral port (with a very small remaining probability) ๐ต it would return "ERROR", even if a few port numbers are still free that would give unique five-tuples ๐ฅบ however, the [Allman] results show that in common scenarios one port choice is enough, and in most cases where more than one choice is needed, two choices suffice ๐ so in those scenarios this won't be a problem, iykyk ๐ โจ
3.3.3. Algorithm 3: Simple Hash-Based Port Selection Algorithm โจ
okay so the goal here is to get the port-reuse vibes of the old BSD algorithm (see Section 2.2) ๐ but also the unpredictability of Algorithm 1 and Algorithm 2 ๐ฎ like, both at once, no cap ๐ซถ
ideally every combo of (local IP address, remote IP addresses, remote port) gets its own "next_ephemeral" value ๐ฆ so the port-reuse frequency is as low as it can possibly be. each of those should start at a random value inside the ephemeral port range ๐ฒ and together they separate the ephemeral port space per destination endpoint ๐ธ (this "separation of the ephemeral port space" just means that transport-protocol instances with different remote endpoints won't walk through the same ephemeral port sequence, unlike the traditional BSD algorithm ๐). BUT keeping all those variables in memory is a lot ๐ so instead we use an offset function F() that can be computed from the local IP address, remote IP address, remote port, and a secret key ๐. F() gives you (practically) different values for each set of arguments, like this:
/* Initialization at system boot time. Could be random. */
next_ephemeral = 0;
/* Ephemeral port selection function */
num_ephemeral = max_ephemeral - min_ephemeral + 1;
offset = F(local_IP, remote_IP, remote_port, secret_key);
count = num_ephemeral;
do {
port = min_ephemeral +
(next_ephemeral + offset) % num_ephemeral;
next_ephemeral++;
if(check_suitable_port(port))
return port;
count--;
} while (count > 0);
return ERROR;
Algorithm 3
so basically F() gives a "per-destination endpoint" fixed offset inside the global ephemeral port range ๐ง both "offset" and "next_ephemeral" can be any value that fits in the storage type, because the resulting port gets restricted in a similar way to Algorithm 1 (see Section 3.3.1) ๐ that means you can just increment "next_ephemeral" and let the unsigned integer wrap around on its own ๐
the F() function should be a cryptographic hash function like MD5 [RFC1321] ๐ it should use both IP addresses, the remote port, and the secret key to compute the offset. the remote IP address is the main separator ๐ so it has to be in the offset calculation. the local IP address and remote port might sometimes be constant, which means they won't add much separation, but they should still be included anyway ๐
stronger crypto than MD5 isn't really needed here, since Algorithm 3 is just a way to obfuscate which ephemeral ports get picked ๐ the secret should be as random as possible (see [RFC4086] for tips on picking secrets) ๐ฒ
on multiuser systems, F() could also take user-specific info, so you get protection not just host-to-host but user-to-service too ๐ญ honestly any identifier for the remote entity could work, depending on what's available and how granular you want it. with SCTP, both hostnames and alternative IP addresses can show up in the association negotiation, and either of those could go into F() ๐
when you have multiple unique identifiers, any of them can be the input to F(), since they all uniquely identify the remote entity ๐ BUT with SCTP the ephemeral port has to be unique across all IP address combos, so ideally you'd always use the same IP address to get one starting offset for every association with a given remote entity, which keeps collisions down ๐ซถ sorting the IP addresses numerically and always picking the lowest would do it. still, since most protocols report the same IP addresses in the same order every time, the sorting probably isn't needed and you can just use the "first one" โจ
whether hostnames can uniquely define hosts is up for debate ๐ค but since SCTP always includes at least one IP address, we recommend using that as the input to F() and ignoring hostname chunks when searching for ephemeral ports ๐
now for the sus part ๐ this algorithm uses a global counter ("next_ephemeral") to pick ephemeral ports, so if an attacker could force a client to keep making new TCP connections to an attacker-controlled machine (or through a path the attacker can observe), they could subtract consecutive source port values to figure out how many outgoing TCP connections the target host set up globally in that time window ๐ฑ (ignoring wrap-around issues and five-tuple collisions, of course) ๐
3.3.4. Algorithm 4: Double-Hash Port Selection Algorithm ๐ธ
this is a trade-off between keeping one single global "next_ephemeral" variable and keeping 2**N "next_ephemeral" variables (where N is the width of F()'s result) ๐ the system keeps an array of TABLE_LENGTH short integers, which splits up the increments of "next_ephemeral" ๐ง here's how it fits into Algorithm 3:
/* Initialization at system boot time */
for(i = 0; i < TABLE_LENGTH; i++)
table[i] = random() % 65536;
/* Ephemeral port selection function */
num_ephemeral = max_ephemeral - min_ephemeral + 1;
offset = F(local_IP, remote_IP, remote_port, secret_key1);
index = G(local_IP, remote_IP, remote_port, secret_key2);
count = num_ephemeral;
do {
port = min_ephemeral + (offset + table[index]) % num_ephemeral;
table[index]++;
if(check_suitable_port(port))
return port;
count--;
} while (count > 0);
return ERROR;
Algorithm 4
"table[]" can be initialized with mathematically random values, as the pseudo-code init above shows ๐ฒ the G() function should be a cryptographic hash function like MD5 [RFC1321] ๐ it should use both IP addresses, the remote port, and a secret key to compute a value from 0 to (TABLE_LENGTH-1). alternatively, G() can take "offset" as input and XOR all the bytes in "offset" together ๐ง
the "table[]" array makes sure that successive transport-protocol instances with the same remote endpoint use increasing ephemeral port numbers ๐ but since the increments are split across TABLE_LENGTH different spaces, the port-reuse frequency is (probabilistically) lower than Algorithm 3's ๐ a new instance for one remote endpoint won't necessarily bump the "next_ephemeral" value for other endpoints โจ
it's kinda iconic that the size of "table[]" doesn't limit how many port sequences you can have ๐ it just splits the increments into TABLE_LENGTH different spaces. the port sequence comes from adding the matching "table[]" entry to "offset", which picks the actual sequence (same as Algorithm 3) ๐ฆ [Allman] found that a TABLE_LENGTH of 10 can beat Algorithm 3 ๐ and making TABLE_LENGTH bigger increases how unpredictable the port number is, and might lower the collision rate even more ๐
an attacker can do traffic analysis on any "increment space" they have "visibility" into ๐ meaning they can force the client to open a transport-protocol instance whose G(offset) lands on the target "increment space". BUT their ability to do traffic analysis is way reduced compared to the traditional BSD algorithm (see Section 2.2) and Algorithm 3 ๐ an implementation can also shrink that even more by splitting the increment space further (aka using a bigger TABLE_LENGTH) ๐ก๏ธ
3.3.5. Algorithm 5: Random-Increments Port Selection Algorithm ๐ข
[Allman] came up with another port selection algorithm that's kind of the middle ground ๐ between algorithms that pick ephemeral ports totally at random (like Sections 3.3.1 and 3.3.2) and algorithms that get obfuscation with less randomization (like Sections 3.3.3 and 3.3.4) โจ
/* Initialization code at system boot time. */
next_ephemeral = random() % 65536; /* Initialization value */
N = 500; /* Determines the trade-off */
/* Ephemeral port selection function */
num_ephemeral = max_ephemeral - min_ephemeral + 1;
count = num_ephemeral;
do {
next_ephemeral = next_ephemeral + (random() % N) + 1;
port = min_ephemeral + (next_ephemeral % num_ephemeral);
if(check_suitable_port(port))
return port;
count--;
} while (count > 0);
return ERROR;
Algorithm 5
this one is about getting a monotonically increasing sequence so instance-ids don't collide ๐ while skipping fixed increments, which would make the sequences trivially predictable ๐ โโ๏ธ the value "N" lets you directly control the trade-off between unpredictability and port-reuse frequency ๐๏ธ the smaller N is, the more it acts like the traditional BSD algorithm (see Section 2.2) ๐ฅน the bigger N is, the more it acts like the algorithm in Section 3.3.1 of this document ๐
when the port numbers wrap around, there's a risk of instance-id collisions ๐ฌ so "N" should be picked according to these criteria:
- It should maximize the wrapping time of the ephemeral port space.
- It should minimize collisions of instance-ids.
- It should maximize the unpredictability of selected port numbers.
these goals literally compete with each other ๐ so picking N is a trade-off. that's why N should be configurable, so system administrators can make the call for themselves ๐ซถ
3.4. Secret-Key Considerations for Hash-Based Port Selection Algorithms ๐
okay so every complex manipulation (like MD5) is only as secure as what you feed it, and for ephemeral ports that input is the secret key ๐ค. if an attacker knows which cryptographic hash function the victim is using (and we should totally expect that), and they can collect enough material, like the ephemeral ports the victim already picked, they can just brute-force the entire secret-key space to find matches. not it ๐
so to protect against this, the secret key needs to be a reasonable length. 128-bit keys should be adequate ๐โจ
another option is to change the key after some time. if the host platform can make reasonably good random data, the secret key can be changed automatically ๐ฆ
BUT here's the tea: changing the secret causes abrupt shifts in the chosen ephemeral ports, so collisions may happen. when the secret changes, the "offset" value (see Sections 3.3.3 and 3.3.4) for each destination endpoint comes out different from what the old secret would've computed, and that can lead to picking a port number that was recently used for connecting to the same endpoint ๐ญ
so the key change has to be done with some thought, and it could happen whenever one of these events occurs:
- The system is being bootstrapped. ๐ฅพ
- Some predefined/random time has expired. โฐ
- The secret key has been used sufficiently often that it should be regarded as insecure now. ๐จ
- There are few active transport-protocol instances (i.e., possibility of a collision is low). ๐ซง
- System load is low (i.e., the performance overhead of local collisions is tolerated). ๐
- There is enough random data available to change the secret key (pseudo-random changes should not be done). ๐ฒ
3.5. Choosing an Ephemeral Port Selection Algorithm ๐
okay so [Allman] is an empirical study of the properties of the algorithms in this document, and it found that all of them offer low collision rates, at most 0.3% ๐. in the network scenarios [Allman] checked, every algorithm here performs well when it comes to instance-id collisions. BUT these results can vary depending on the characteristics of network traffic and the specific network setup, so don't just assume ๐ค
Section 2.2's algorithm is the traditional one used in BSD-derived systems. it generates a global sequence of ephemeral port numbers, which makes it trivial for an attacker to predict the port that a future transport protocol instance will use ๐. it is very simple though, and it leads to a low port-reuse frequency ๐ธ
Algorithm 1 and Algorithm 2 actually randomize the ephemeral ports, which is the big win ๐ฒ. the downside is they may increase the chances of port number collisions, and that could make a connection establishment attempt fail. [Allman] found these two show the largest collision rates of all the algorithms here ๐ฌ
Algorithm 3 gives complete separation in local and remote IP addresses and in the remote port space, and only limited separation in other dimensions (see Section 3.4). the thing to keep in mind is that implementations should consider the performance impact of computing the cryptographic hash used for the offset ๐งฎ
Algorithm 4 improves on Algorithm 3 and usually leads to a lower port-reuse frequency. the cost is more processor cycles spent computing G(), plus extra kernel memory for storing the array "table[]" ๐ง
Algorithm 5 is kind of the middle ground between the simple randomization algorithms (Algorithm 1 and Algorithm 2) and the hash-based ones (Algorithm 3 and Algorithm 4) โ๏ธ. the upper limit on the random increments (the value "N" in the pseudo-code in Section 3.3.5) is what controls the trade-off between randomization and port-reuse frequency ๐
last but definitely not least, there's a special case that might rule out Algorithm 3 and Algorithm 4, and it needs a look ๐. some applications contain this code sequence:
s = socket();
bind(s, IP_address, port = *);
on some BSD-derived systems, the call to bind() results in an ephemeral port number being selected. but neither the remote IP address nor the remote port is available to the ephemeral port selection function at that point. so the hash function F() used in Algorithm 3 and Algorithm 4 won't have all the arguments it needs, and the result becomes impossible to compute ๐ฑ. transport protocols implementing Algorithm 3 or Algorithm 4 should consider using Algorithm 2 in exactly this scenario.
there's an alternative, which is "lazy binding" in response to the bind() call. basically the selection of an ephemeral port gets delayed until something like connect() or send() is called. by then the port really is selected, all the arguments for F() are available, and Algorithm 3 and Algorithm 4 can still be used in this scenario ๐ช. Linux has implemented this approach [Linux] โจ
4. Interaction with Network Address Port Translation (NAPT) โจ
okay so Network Address Port Translation (NAPT) translates both the network address and the transport-protocol port number. this lets the transport identifiers of a bunch of private hosts get multiplexed into the transport identifiers of a single external address [RFC2663] ๐
when a NAPT sits between the two endpoints of a transport-protocol instance, how well the ephemeral port selection is obfuscated (from the point of view of the external network) depends on the ephemeral port selection function at the NAPT ๐ซถ. so NAPTs should consider obfuscating their ephemeral port selection using any of the algorithms discussed in this document ๐
A NAPT that does not implement port preservation [RFC4787] [RFC5382] SHOULD obfuscate the selection of the ephemeral port of a packet whenever that port gets changed during translation of the packet ๐ฅ
A NAPT that does implement port preservation SHOULD obfuscate the ephemeral port of a packet only if the port has to be changed because it's already in use for some other session ๐
A NAPT that performs parity preservation and has to change the ephemeral port during translation of a packet SHOULD obfuscate the ephemeral ports. the algorithms in this document could be easily adapted so parity is preserved (i.e., force the lowest order bit of the resulting port number to 0 or 1 depending on whether even or odd parity is desired) ๐
some applications allocate contiguous ports and expect to see contiguous ports in use at their peers. ngl that expectation can be hard to meet at a NAPT, since some port numbers might already be in use by other sessions. then an alternative port might need to be selected, which gives a non-contiguous port number sequence (see Section 4.2.3 of [RFC4787]) ๐ญ. a NAPT that implements a simple port randomization algorithm (such as Algorithm 1, Algorithm 2, or Algorithm 5) is likely to break this assumption, even if the endpoint selecting the ephemeral port does pick contiguous ports. BUT since deployed NAPTs have implemented a bunch of different ephemeral port selection algorithms, any application relying on one specific algorithm at the NAPT is likely to hit interoperability problems when a NAPT is between the two endpoints ๐ฅฒ
the good news is some of the algorithms here (namely Algorithm 3 and Algorithm 4) pick consecutive ephemeral ports so they're contiguous, except when one of the port numbers needed for a contiguous sequence is already in use by some other NAPT session. so a NAPT that wants to produce contiguous port sequences should consider implementing Algorithm 3 or Algorithm 4 ๐ง Section 3.5 has more guidance on choosing a port selection algorithm.
it's also worth knowing that in some network scenarios, a NAPT may naturally obscure ephemeral port selections, just because of the huge range of services it connects to and the overall rate of the traffic [Allman] ๐
5. Security Considerations ๐ก๏ธ
okay this part is SERIOUS bestie ๐ค obfuscating the ephemeral port selection is NO replacement for cryptographic mechanisms, like IPsec [RFC4301], when it comes to protecting transport-protocol instances against blind attacks ๐
An eavesdropper who can monitor the packets for the transport-protocol instance being attacked can learn the IP addresses and port numbers in use (and also sequence numbers, etc.) and easily perform an attack. obfuscating the ephemeral port selection gives zero extra protection against this kind of attack. in these situations, proper authentication mechanisms like the ones described in [RFC4301] should be used ๐จ
this spec recommends including the whole range 1024-65535 for the selection of ephemeral ports, and suggests that an implementation keeps a list of port numbers that should NOT be made available for ephemeral port selection ๐. if that list of unavailable ports is significant, Algorithm 1 may be highly biased and generate predictable ports, as noted in Section 3.3.1 ๐ฌ. in particular, if the list of IANA Registered Ports is accepted as the local list of port numbers that shouldn't be available, certain ports may end up with 500 times the probability of other ports ๐. systems that support lots of applications (resulting in large lists of unavailable ports), or that use the IANA Registered Ports without modification, MUST NOT use Algorithm 1 ๐ โโ๏ธ
if the local offset function F() (in Algorithm 3 and Algorithm 4) gives identical offsets for different inputs more often than chance would predict, the port-offset mechanism proposed in this document would have a reduced effect ๐
if random numbers are the only source of the secret key, they should be chosen following the recommendations in [RFC4086] ๐ฒ
if an attacker uses dynamically assigned IP addresses, the current ephemeral port offset (Algorithm 3 and Algorithm 4) for a given five-tuple can be sampled and then used to attack an innocent peer reusing that address ๐ฑ. BUT this only works until a re-keying happens as described above. also, since ephemeral ports are only used on the client side (e.g., the one initiating the transport-protocol communication), both the attacker and the new peer need to act as servers in the scenario just described. servers using dynamic IP addresses do exist, but they're not very common, and with an appropriate re-keying mechanism the effect of this attack is limited ๐งฏโจ
6. Acknowledgements โจ
The offset function used in Algorithm 3 and Algorithm 4 was inspired by the mechanism Steven Bellovin proposed in [RFC1948] for defending against TCP sequence number attacks. Big thanks to Steven! ๐ซถ
The authors would like to thank (in alphabetical order) Mark Allman, Jari Arkko, Matthias Bethke, Stephane Bortzmeyer, Brian Carpenter, Vincent Deffontaines, Ralph Droms, Lars Eggert, Pasi Eronen, Gorry Fairhurst, Adrian Farrel, Guillermo Gont, David Harrington, Alfred Hoenes, Avshalom Houri, Charlie Kaufman, Amit Klein, Subramanian Moonesamy, Carlos Pignataro, Tim Polk, Kacheong Poon, Pasi Sarolahti, Robert Sparks, Randall Stewart, Joe Touch, Michael Tuexen, Magnus Westerlund, and Dan Wing for their valuable feedback on draft versions of this document. The whole squad showed up ๐๐
The authors would like to thank Alfred Hoenes for his admirable effort in improving the quality of this document. Alfred really went in on the editing, bestie ๐ง
The authors would like to thank FreeBSD's Mike Silbersack for a very fruitful discussion about ephemeral port selection techniques. ๐ฆ
Fernando Gont's attendance at IETF meetings was supported by ISOC's "Fellowship to the IETF" program. Love that for him ๐ธ
7. References ๐
7.1. Normative References
[RFC0768] Postel, J., "User Datagram Protocol", STD 6, RFC 768,
August 1980.
[RFC0793] Postel, J., "Transmission Control Protocol", STD 7,
RFC 793, September 1981.
[RFC1321] Rivest, R., "The MD5 Message-Digest Algorithm",
RFC 1321, April 1992.
[RFC2119] Bradner, S., "Key words for use in RFCs to Indicate
Requirement Levels", BCP 14, RFC 2119, March 1997.
[RFC2385] Heffernan, A., "Protection of BGP Sessions via the TCP
MD5 Signature Option", RFC 2385, August 1998.
[RFC3550] Schulzrinne, H., Casner, S., Frederick, R., and V.
Jacobson, "RTP: A Transport Protocol for Real-Time
Applications", STD 64, RFC 3550, July 2003.
[RFC3605] Huitema, C., "Real Time Control Protocol (RTCP)
attribute in Session Description Protocol (SDP)",
RFC 3605, October 2003.
[RFC3828] Larzon, L-A., Degermark, M., Pink, S., Jonsson, L-E.,
and G. Fairhurst, "The Lightweight User Datagram
Protocol (UDP-Lite)", RFC 3828, July 2004.
[RFC4086] Eastlake, D., Schiller, J., and S. Crocker,
"Randomness Requirements for Security", BCP 106,
RFC 4086, June 2005.
[RFC4301] Kent, S. and K. Seo, "Security Architecture for the
Internet Protocol", RFC 4301, December 2005.
[RFC4340] Kohler, E., Handley, M., and S. Floyd, "Datagram
Congestion Control Protocol (DCCP)", RFC 4340,
March 2006.
[RFC4787] Audet, F. and C. Jennings, "Network Address
Translation (NAT) Behavioral Requirements for Unicast
UDP", BCP 127, RFC 4787, January 2007.
[RFC4960] Stewart, R., "Stream Control Transmission Protocol",
RFC 4960, September 2007.
[RFC5382] Guha, S., Biswas, K., Ford, B., Sivakumar, S., and P.
Srisuresh, "NAT Behavioral Requirements for TCP",
BCP 142, RFC 5382, October 2008.
7.2. Informative References
[Allman] Allman, M., "Comments On Selecting Ephemeral Ports",
ACM Computer Communication Review, 39(2), 2009.
[CPNI-TCP] Gont, F., "CPNI Technical Note 3/2009: Security
Assessment of the Transmission Control Protocol
(TCP)", 2009, <http://www.cpni.gov.uk/Docs/
tn-03-09-security-assessment-TCP.pdf>.
[FreeBSD] The FreeBSD Project, <http://www.freebsd.org>.
[IANA] "IANA Port Numbers",
<http://www.iana.org/assignments/port-numbers>.
[Linux] The Linux Project, <http://www.kernel.org>.
[NetBSD] The NetBSD Project, <http://www.netbsd.org>.
[OpenBSD] The OpenBSD Project, <http://www.openbsd.org>.
[OpenSolaris] OpenSolaris, <http://www.opensolaris.org>.
[RFC1337] Braden, B., "TIME-WAIT Assassination Hazards in TCP",
RFC 1337, May 1992.
[RFC1948] Bellovin, S., "Defending Against Sequence Number
Attacks", RFC 1948, May 1996.
[RFC2663] Srisuresh, P. and M. Holdrege, "IP Network Address
Translator (NAT) Terminology and Considerations",
RFC 2663, August 1999.
[RFC4953] Touch, J., "Defending TCP Against Spoofing Attacks",
RFC 4953, July 2007.
[RFC5925] Touch, J., Mankin, A., and R. Bonica, "The TCP
Authentication Option", RFC 5925, June 2010.
[RFC5927] Gont, F., "ICMP Attacks against TCP", RFC 5927,
July 2010.
[SCTP-SOCKET] Stewart, R., Poon, K., Tuexen, M., Lei, P., and V.
Yasevich, V., "Sockets API Extensions for Stream
Control Transmission Protocol (SCTP)", Work in
Progress, January 2011.
[Silbersack] Silbersack, M., "Improving TCP/IP security through
randomization without sacrificing interoperability",
EuroBSDCon 2005 Conference.
[Stevens] Stevens, W., "Unix Network Programming, Volume 1:
Networking APIs: Socket and XTI", Prentice Hall, 1998.
[TCP-SEC] Gont, F., "Security Assessment of the Transmission
Control Protocol (TCP)", Work in Progress,
February 2010.
[Watson] Watson, P., "Slipping in the Window: TCP Reset
Attacks", CanSecWest 2004 Conference.
Appendix A. Survey of the Algorithms in Use by Some Popular Implementations ๐
A.1. FreeBSD
FreeBSD 8.0 implements Algorithm 1, and in response to this document it now uses a "min_port" of 10000 and a "max_port" of 65535 [FreeBSD]. ๐ก
A.2. Linux
Linux 2.6.15-53-386 implements Algorithm 3, with MD5 as the hash algorithm. If the algorithm is faced with the corner-case scenario described in Section 3.5, Algorithm 1 is used instead [Linux]. ๐ง
A.3. NetBSD
NetBSD 5.0.1 does not obfuscate its ephemeral port numbers. It selects ephemeral port numbers from the range 49152-65535, starting from port 65535, and decreasing the port number for each ephemeral port number selected [NetBSD]. Super straightforward, no hiding ๐
A.4. OpenBSD
OpenBSD 4.2 implements Algorithm 1, with a "min_port" of 1024 and a "max_port" of 49151. [OpenBSD] ๐ก
A.5. OpenSolaris
OpenSolaris 2009.06 implements Algorithm 1, with a "min_port" of 32768 and a "max_port" of 65535 [OpenSolaris]. โ๏ธ
Authors' Addresses ๐
Michael Vittrup Larsen
Tieto
Skanderborgvej 232
Aarhus DK-8260
Denmark
Phone: +45 8938 5100
EMail: michael.larsen@tieto.com
Fernando Gont
Universidad Tecnologica Nacional / Facultad Regional Haedo
Evaristo Carriego 2644
Haedo, Provincia de Buenos Aires 1706
Argentina
Phone: +54 11 4650 8472
EMail: fernando@gont.com.ar
๐ฆ where this page comes from
- ietf: rfc6056 2011-01
- ๐ girlified by claude-haiku-5-5 on : a language model's rewrite of the original, under its terms