Internet-Draft | Generation of Transient Numeric IDs | July 2022 |
Gont & Arce | Expires 12 January 2023 | [Page] |
This document performs an analysis of the security and privacy implications of different types of "transient numeric identifiers" used in IETF protocols, and tries to categorize them based on their interoperability requirements and their associated failure severity when such requirements are not met. Subsequently, it provides advice on possible algorithms that could be employed to satisfy the interoperability requirements of each identifier category, while minimizing the negative security and privacy implications, thus providing guidance to protocol designers and protocol implementers. Finally, it describes a number of algorithms that have been employed in real implementations to generate transient numeric identifiers, and analyzes their security and privacy properties. This document is a product of the Privacy Enhancement and Assessment Research Group (PEARG) in the IRTF.¶
This Internet-Draft is submitted in full conformance with the provisions of BCP 78 and BCP 79.¶
Internet-Drafts are working documents of the Internet Engineering Task Force (IETF). Note that other groups may also distribute working documents as Internet-Drafts. The list of current Internet-Drafts is at https://datatracker.ietf.org/drafts/current/.¶
Internet-Drafts are draft documents valid for a maximum of six months and may be updated, replaced, or obsoleted by other documents at any time. It is inappropriate to use Internet-Drafts as reference material or to cite them other than as "work in progress."¶
This Internet-Draft will expire on 12 January 2023.¶
Copyright (c) 2022 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 (https://trustee.ietf.org/license-info) in effect on the date of publication of this document. Please review these documents carefully, as they describe your rights and restrictions with respect to this document.¶
Networking protocols employ a variety of transient numeric identifiers for different protocol objects, such as IPv4 and IPv6 Fragment Identifiers [RFC0791] [RFC8200], IPv6 Interface Identifiers (IIDs) [RFC4291], transport protocol ephemeral port numbers [RFC6056], TCP Initial Sequence Numbers (ISNs) [RFC0793], and DNS Query IDs [RFC1035]. These identifiers usually have specific interoperability requirements (e.g. uniqueness during a specified period of time) that must be satisfied such that they do not result in negative interoperability implications, and an associated failure severity when such requirements are not met, ranging from soft to hard failures.¶
For more than 30 years, a large number of implementations of IETF protocols have been subject to a variety of attacks, with effects ranging from Denial of Service (DoS) or data injection, to information leakages that could be exploited for pervasive monitoring [RFC7258]. The root cause of these issues has been, in many cases, the poor selection of transient numeric identifiers in such protocols, usually as a result of insufficient or misleading specifications. While it is generally trivial to identify an algorithm that can satisfy the interoperability requirements of a given transient numeric identifier, empirical evidence exists that doing so without negatively affecting the security and/or privacy properties of the aforementioned protocols is prone to error [I-D.irtf-pearg-numeric-ids-history].¶
For example, implementations have been subject to security and/or privacy issues resulting from:¶
Recent history indicates that when new protocols are standardized or new protocol implementations are produced, the security and privacy properties of the associated transient numeric identifiers tend to be overlooked, and inappropriate algorithms to generate transient numeric identifiers are either suggested in the specifications or selected by implementers. As a result, it should be evident that advice in this area is warranted.¶
We note that the use of cryptographic techniques may readily mitigate some of the issues arising from predictable transient numeric identifiers. For example, cryptographic integrity and authentication can readily mitigate data injection attacks even in the presence of predictable transient numeric identifiers (such as "sequence numbers"). However, use of flawed algorithms (such as global counters) for generating transient numeric identifiers could still result in information leakages even when cryptographic techniques are employed.¶
This document contains a non-exhaustive survey of transient numeric identifiers employed in various IETF protocols, and aims to categorize such identifiers based on their interoperability requirements, and the associated failure severity when such requirements are not met. Subsequently, it provides advice on possible algorithms that could be employed to satisfy the interoperability requirements of each category, while minimizing negative security and privacy implications. Finally, it analyzes several algorithms that have been employed in real implementations to meet such requirements, and analyzes their security and privacy properties.¶
This document represents the consensus of the Privacy Enhancement and Assessment Research Group (PEARG).¶
Throughout this document, we assume an attacker does not have physical or logical access to the system(s) being attacked, and that the attacker can only observe traffic explicitly directed to the attacker. For example, an attacker cannot observe traffic transferred between a sender and the receiver(s) of a target protocol, but may be able to interact with any of these entities, including by e.g. sending any traffic to them to sample transient numeric identifiers employed by the target systems when communicating with the attacker.¶
For example, when analyzing vulnerabilities associated with TCP Initial Sequence Numbers (ISNs), we consider the attacker is unable to capture network traffic corresponding to a TCP connection between two other hosts. However, we consider the attacker is able to communicate with any of these hosts (e.g., establish a TCP connection with any of them), to e.g. sample the TCP ISNs employed by these systems when communicating with the attacker.¶
Similarly, when considering host-tracking attacks based on IPv6 interface identifiers, we consider an attacker may learn the IPv6 address employed by a victim node if e.g. the address becomes exposed as a result of the victim node communicating with an attacker-operated server. Subsequently, an attacker may perform host-tracking by probing a set of target addresses composed by a set of target prefixes and the IPv6 interface identifier originally learned by the attacker. Alternatively, an attacker may perform host tracking if e.g. the victim node communicates with an attacker-operated server as it moves from one location to another, those exposing its configured addresses. We note that none of these scenarios requires the attacker observe traffic not explicitly directed to the attacker.¶
While assessing protocol specifications regarding the use of transient numeric identifiers, we have found that most of the issues discussed in this document arise as a result of one of the following conditions:¶
A number of protocol specifications (too many of them) have simply overlooked the security and privacy implications of transient numeric identifiers [I-D.irtf-pearg-numeric-ids-history]. Examples of them are the specification of TCP ephemeral ports in [RFC0793], the specification of TCP sequence numbers in [RFC0793], or the specification of the DNS Query ID in [RFC1035].¶
On the other hand, there are a number of protocol specifications that over-specify some of their associated transient numeric identifiers. For example, [RFC4291] essentially overloads the semantics of IPv6 Interface Identifiers (IIDs) by embedding link-layer addresses in the IPv6 IIDs, when the interoperability requirement of uniqueness could be achieved in other ways that do not result in negative security and privacy implications [RFC7721]. Similarly, [RFC2460] suggested the use of a global counter for the generation of Fragment Identification values, when the interoperability properties of uniqueness per {IPv6 Source Address, IPv6 Destination Address} could be achieved with other algorithms that do not result in negative security and privacy implications [RFC7739].¶
Finally, there are protocol implementations that simply fail to comply with existing protocol specifications. For example, some popular operating systems (notably Microsoft Windows) still fail to implement transport protocol ephemeral port randomization, as recommended in [RFC6056].¶
Section 2 defines the concept of "Failure Severity", along with two types of failure severities that we employ throughout this document: soft and hard.¶
Our analysis of the severity of a failure is performed from the point of view of the protocol in question. However, the corresponding severity on the upper protocol (or application) might not be the same as that of the protocol in question. For example, a TCP connection that is aborted might or might not result in a hard failure of the upper application: if the upper application can establish a new TCP connection without any impact on the application, a hard failure at the TCP protocol may have no severity at the application level. On the other hand, if a hard failure of a TCP connection results in excessive degradation of service at the application layer, it will also result in a hard failure at the application.¶
This section includes a non-exhaustive survey of transient numeric identifiers, which are representative of all the possible combinations of interoperability requirements and failure severities found in popular protocols from different layers. Additionally, it proposes a number of categories that can accommodate these identifiers based on their interoperability requirements and their associated failure severity (soft or hard).¶
Identifier | Interoperability Requirements | Failure Severity |
---|---|---|
IPv6 Frag ID | Uniqueness (for IP address pair) | Soft/Hard (1) |
IPv6 IID | Uniqueness (and stable within IPv6 prefix) (2) | Soft (3) |
TCP ISN | Monotonically-increasing (4) | Hard (4) |
TCP initial timestamp | Monotonically-increasing (5) | Hard (5) |
TCP eph. port | Uniqueness (for connection ID) | Hard |
IPv6 Flow Label | Uniqueness | None (6) |
DNS Query ID | Uniqueness | None (7) |
NOTE:¶
Based on the survey above, we can categorize identifiers as follows:¶
Cat # | Category | Sample Proto IDs |
---|---|---|
1 | Uniqueness (soft failure) | IPv6 Flow L., DNS Query ID |
2 | Uniqueness (hard failure) | IPv6 Frag ID, TCP ephemeral port |
3 | Uniqueness, stable within context (soft failure) | IPv6 IID |
4 | Uniqueness, monotonically increasing within context (hard failure) | TCP ISN, TCP initial timestamp |
We note that Category #4 could be considered a generalized case of category #3, in which a monotonically increasing element is added to a stable (within context) element, such that the resulting identifiers are monotonically increasing within a specified context. That is, the same algorithm could be employed for both #3 and #4, given appropriate parameters.¶
The following subsections describe some sample algorithms that can be employed for generating transient numeric identifiers for each of the categories above, while mitigating the vulnerabilities analyzed in Section 8 of this document.¶
All of the variables employed in the algorithms of the following subsections are of "unsigned integer" type, except for the "retry" variable, that is of (signed) "integer" type.¶
The requirement of uniqueness with a soft failure severity can be complied with a Pseudo-Random Number Generator (PRNG).¶
While most systems provide access to a PRNG, many of such PRNG implementations are not cryptographically secure, and therefore might be statistically biased or subject to adversarial influence. For example, ISO C [C11] rand(3) implementations are not cryptographically secure.¶
On the other hand, a number of systems provide an interface to a Cryptographically Secure PRNG (CSPRNG) [RFC8937] [RFC4086], which guarantees high entropy, unpredictability, and good statistical distribution of the random values generated. For example, GNU/Linux's CSPRNG implementation is available via the getentropy(3) interface [GETENTROPY], while OpenBSD's CSPRNG implementation is available via the arc4random(3) and arc4random_uniform(3) interfaces [ARC4RANDOM]. Where available, these CSPRNGs should be preferred over e.g. POSIX [POSIX] random(3) or ISO C [C11] rand(3) implementations.¶
In scenarios where a CSPRNG is not readily available to select transient numeric identifiers of Category #1, a security and privacy assessment of employing a regular PRNG should be performed, supporting the implementation decision.¶
We note that since the premise is that collisions of transient numeric identifiers of this category only leads to soft failures, in many cases, the algorithm might not need to check the suitability of a selected identifier (i.e., the suitable_id() function, described below, could always return "true").¶
In scenarios where e.g. simultaneous use of a given numeric ID is undesirable and the implementation detects such condition, an implementation may opt to select the next available identifier in the same sequence, or select another random number. Section 7.1.1 is an implementation of the former strategy, while Section 7.1.2 is an implementation of the later. Typically, the algorithm in Section 7.1.2 results in a more uniform distribution of the generated transient numeric identifiers. However, for transient numeric identifiers where an implementation typically keeps local state about unsuitable/used identifiers, the algorithm in Section 7.1.2 may require many more iterations than the algorithm in Section 7.1.1 to generate a suitable transient numeric identifier. This will usually be affected by the current usage ratio of transient numeric identifiers (i.e., number of numeric identifiers considered suitable / total number of numeric identifiers) and other parameters. Therefore, in such cases many implementations tend to prefer the algorithm in Section 7.1.1 over the algorithm in Section 7.1.2.¶
/* Transient Numeric ID selection function */ id_range = max_id - min_id + 1; next_id = min_id + (random() % id_range); retry = id_range; do { if (suitable_id(next_id)) { return next_id; } if (next_id == max_id) { next_id = min_id; } else { next_id++; } retry--; } while (retry > 0); return ERROR;¶
Assuming the randomness requirements for the PRNG are met (see [RFC4086]), this algorithm does not suffer from any of the issues discussed in Section 8.¶
The following pseudo-code illustrates another algorithm for selecting a random transient numeric identifier which, in the event a selected identifier is found to be unsuitable (e.g., already in use), another identifier is randomly selected:¶
/* Transient Numeric ID selection function */ id_range = max_id - min_id + 1; retry = id_range; do { next_id = min_id + (random() % id_range); if (suitable_id(next_id)) { return next_id; } retry--; } while (retry > 0); return ERROR;¶
This algorithm might be unable to select a transient numeric identifier (i.e., return "ERROR") even if there are suitable identifiers available, in cases where a large number of identifiers are found to be unsuitable (e.g. "in use").¶
The same considerations from Section 7.1.1 with respect to the properties of random() and the adaptation of its output length apply to this algorithm.¶
Assuming the randomness requirements for the PRNG are met (see [RFC4086]), this algorithm does not suffer from any of the issues discussed in Section 8.¶
One of the most trivial approaches for generating unique transient numeric identifier (with a hard failure severity) is to reduce the identifier reuse frequency by generating the numeric identifiers with a monotonically-increasing function (e.g. linear). As a result, any of the algorithms described in Section 7.4 ("Category #4: Uniqueness, monotonically increasing within context (hard failure)") can be readily employed for complying with the requirements of this transient numeric identifier category.¶
In cases where suitability (e.g. uniqueness) of the selected identifiers can be definitely assessed by the local system, any of the algorithms described in Section 7.1 ("Category #1: Uniqueness (soft failure)") can be readily employed for complying with the requirements of this numeric identifier category.¶
The goal of the following algorithm is to produce identifiers that are stable for a given context (identified by "CONTEXT"), but that change when the aforementioned context changes.¶
In order to avoid storing in memory the transient numeric identifiers computed for each CONTEXT, the following algorithm employs a calculated technique (as opposed to keeping state in memory) to generate a stable transient numeric identifier for each given context.¶
/* Transient Numeric ID selection function */ id_range = max_id - min_id + 1; retry = 0; do { offset = F(CONTEXT, retry, secret_key); next_id = min_id + (offset % id_range); if (suitable_id(next_id)) { return next_id; } retry++; } while (retry <= MAX_RETRIES); return ERROR;¶
In this algorithm, the function F() provides a stateless and stable per-CONTEXT offset, where CONTEXT is the concatenation of all the elements that define the given context.¶
F() is a pseudorandom function (PRF). It must not be computable from the outside (without knowledge of the secret key). F() must also be difficult to reverse, such that it resists attempts to obtain the secret_key, even when given samples of the output of F() and knowledge or control of the other input parameters. F() should produce an output of at least as many bits as required for the transient numeric identifier. SipHash-2-4 (128-bit key, 64-bit output) [SipHash] and BLAKE3 (256-bit key, arbitrary-length output) [BLAKE3] are two possible options for F(). Alternatively, F() could be implemented with a keyed-hash message authentication code (HMAC) [RFC2104]. HMAC-SHA-256 [FIPS-SHS] would be one possible option for such implementation alternative. Note: Use of HMAC-MD5 [RFC1321] is not recommended for F() [RFC6151].¶
The result of F() is no more secure than the secret key, and therefore 'secret_key' must be unknown to the attacker, and must be of a reasonable length. 'secret_key' must remain stable for a given CONTEXT, since otherwise the numeric identifiers generated by this algorithm would not have the desired stability properties (i.e., stable for a given CONTEXT). In most cases, 'secret_key' should be selected with a PRNG (see [RFC4086] for recommendations on choosing secrets) at an appropriate time, and stored in stable or volatile storage (as necessary) for future use.¶
The result of F() is stored in the variable 'offset', which may take any value within the storage type range, since we are restricting the resulting identifier to be in the range [min_id, max_id] in a similar way as in the algorithm described in Section 7.1.1.¶
suitable_id() checks whether the candidate identifier has suitable uniqueness properties. Collisions (i.e., an identifier that is not unique) are recovered by incrementing the 'retry' variable and recomputing F(), up to a maximum of MAX_RETRIES times. However, recovering from collisions will usually result in identifiers that fail to remain constant for the specified context. This is normally acceptable when the probability of collisions is small, as in the case of e.g. IPv6 IIDs resulting from SLAAC [RFC7217] [RFC8981].¶
For obvious reasons, the transient numeric identifiers generated with this algorithm allow for network activity correlation and fingerprinting within "CONTEXT". However, this is essentially a design goal of this category of transient numeric identifiers.¶
One possible way of selecting unique monotonically-increasing identifiers (per context) is to employ a per-context counter. Such an algorithm could be described as follows:¶
/* Transient Numeric ID selection function */ id_range = max_id - min_id + 1; retry = id_range; id_inc = increment() % id_range; if( (next_id = lookup_counter(CONTEXT)) == ERROR){ next_id = min_id + random() % id_range; } do { if ( (max_id - next_id) >= id_inc){ next_id = next_id + id_inc; } else { next_id = min_id + id_inc - (max_id - next_id); } if (suitable_id(next_id)){ store_counter(CONTEXT, next_id); return next_id; } retry = retry - id_inc; } while (retry > 0); return ERROR;¶
Essentially, whenever a new identifier is to be selected, the algorithm checks whether a counter for the corresponding context exists. If does, the value of such counter is incremented to obtain the new transient numeric identifier, and the counter is updated. If no counter exists for such context, a new counter is created and initialized to a random value, and used as the selected transient numeric identifier. This algorithm produces a per-context counter, which results in one monotonically-increasing function for each context. Since each counter is initialized to a random value, the resulting values are unpredictable by an off-path attacker.¶
The choice of id_inc has implications on both the security and privacy properties of the resulting identifiers, but also on the corresponding interoperability properties. On one hand, minimizing the increments generally minimizes the identifier reuse frequency, albeit at increased predictability. On the other hand, if the increments are randomized, predictability of the resulting identifiers is reduced, and the information leakage produced by global constant increments is mitigated. However, using larger increments than necessary can result in higher numeric ID reuse frequency.¶
This algorithm has the following drawbacks:¶
Otherwise, the identifiers produced by this algorithm do not suffer from the other issues discussed in Section 8.¶
The goal of this algorithm is to produce monotonically-increasing transient numeric identifiers (for each given context), with a randomized initial value. For example, if the identifiers being generated must be monotonically-increasing for each {IP Source Address, IP Destination Address} set, then each possible combination of {IP Source Address, IP Destination Address} should have a separate monotonically-increasing sequence, that starts at a different random value.¶
Instead of maintaining a per-context counter (as in the algorithm from Section 7.4.1), the following algorithm employs a calculated technique to maintain a random offset for each possible context.¶
/* Initialization code */ counter = 0; /* Transient Numeric ID selection function */ id_range = max_id - min_id + 1; id_inc = increment() % id_range; offset = F(CONTEXT, secret_key); retry = id_range; do { next_id = min_id + (offset + counter) % id_range; counter = counter + id_inc; if (suitable_id(next_id)) { return next_id; } retry = retry - id_inc; } while (retry > 0); return ERROR;¶
In the algorithm above, the function F() provides a (stateless) unpredictable offset for each given context (as identified by 'CONTEXT').¶
F() is a PRF, with the same properties as those specified for F() in Section 7.3.¶
CONTEXT is the concatenation of all the elements that define a given context. For example, if this algorithm is expected to produce identifiers that are monotonically-increasing for each set (Source IP Address, Destination IP Address), CONTEXT should be the concatenation of these two IP addresses.¶
The function F() provides a "per-CONTEXT" fixed offset within the numeric identifier "space". Both the 'offset' and 'counter' variables may take any value within the storage type range since we are restricting the resulting identifier to be in the range [min_id, max_id] in a similar way as in the algorithm described in Section 7.1.1. This allows us to simply increment the 'counter' variable and rely on the unsigned integer to wrap around.¶
The result of F() is no more secure than the secret key, and therefore 'secret_key' must be unknown to the attacker, and must be of a reasonable length. 'secret_key' must remain stable for a given CONTEXT, since otherwise the numeric identifiers generated by this algorithm would not have the desired stability properties (i.e., monotonically-increasing for a given CONTEXT). In most cases, 'secret_key' should be selected with a PRNG (see [RFC4086] for recommendations on choosing secrets) at an appropriate time, and stored in stable or volatile storage (as necessary) for future use.¶
It should be noted that, since this algorithm uses a global counter ("counter") for selecting identifiers (i.e., all counters share the same increments space), this algorithm results in an information leakage (as described in Section 8.2). For example, if this algorithm were used for selecting TCP ephemeral ports, and an attacker could force a client to periodically establish a new TCP connection to an attacker-controlled system (or through an attacker-observable routing path), the attacker could subtract consecutive source port values to obtain the number of outgoing TCP connections established globally by the victim host within that time period (up to wrap-around issues and five-tuple collisions, of course). This information leakage could be partially mitigated by employing small random values for the increments (i.e., increment() function), instead of having increment() return the constant "1".¶
We nevertheless note that an improved mitigation of this information leakage could be more successfully achieved by employing the algorithm from Section 7.4.3, instead.¶
A trade-off between maintaining a single global 'counter' variable and maintaining 2**N 'counter' variables (where N is the width of the result of F()), could be achieved as follows. The system would keep an array of TABLE_LENGTH values, which would provide a separation of the increment space into multiple buckets. This improvement could be incorporated into the algorithm from Section 7.4.2 as follows:¶
/* Initialization code */ for(i = 0; i < TABLE_LENGTH; i++) { table[i] = random(); } /* Transient Numeric ID selection function */ id_range = max_id - min_id + 1; id_inc = increment() % id_range; offset = F(CONTEXT, secret_key1); index = G(CONTEXT, secret_key2) % TABLE_LENGTH; retry = id_range; do { next_id = min_id + (offset + table[index]) % id_range; table[index] = table[index] + id_inc; if (suitable_id(next_id)) { return next_id; } retry = retry - id_inc; } while (retry > 0); return ERROR;¶
'table[]' could be initialized with random values, as indicated by the initialization code in the pseudo-code above.¶
Both F() and G() are PRFs, with the same properties as those required for F() in Section 7.3.¶
The results of F() and G() are no more secure than their respective secret keys ('secret_key1' and 'secret_key2', respectively), and therefore both secret keys must be unknown to the attacker, and must be of a reasonable length. Both secret keys must remain stable for the given CONTEXT, since otherwise the transient numeric identifiers generated by this algorithm would not have the desired stability properties (i.e., monotonically-increasing for a given CONTEXT). In most cases, both secret keys should be selected with a PRNG (see [RFC4086] for recommendations on choosing secrets) at an appropriate time, and stored in stable or volatile storage (as necessary) for future use.¶
The 'table[]' array assures that successive transient numeric identifiers for a given context will be monotonically-increasing. Since the increments space is separated into TABLE_LENGTH different spaces, the identifier reuse frequency will be (probabilistically) lower than that of the algorithm in Section 7.4.2. That is, the generation of an identifier for one given context will not necessarily result in increments in the identifier sequence of other contexts. It is interesting to note that the size of 'table[]' does not limit the number of different identifier sequences, but rather separates the *increment space* into TABLE_LENGTH different spaces. The selected transient numeric identifier sequence will be obtained by adding the corresponding entry from 'table[]' to the value in the 'offset' variable, which selects the actual identifier sequence space (as in the algorithm from Section 7.4.2).¶
An attacker can perform traffic analysis for any "increment space" (i.e., context) into which the attacker has "visibility" -- namely, the attacker can force a system to generate identifiers for G(CONTEXT, secret_key2), where the result of G() identifies the target "increment space". However, the attacker's ability to perform traffic analysis is very reduced when compared to the simple PRF-based identifiers (described in Section 7.4.2) and the predictable linear identifiers (described in Appendix A.1). Additionally, an implementation can further limit the attacker's ability to perform traffic analysis by further separating the increment space (that is, using a larger value for TABLE_LENGTH) and/or by randomizing the increments (i.e., increment() returning a small random number as opposed to the constant "1").¶
Otherwise, this algorithm does not suffer from the issues discussed in Section 8.¶
An identifier that is predictable within a given context allows for network activity correlation within that context.¶
For example, a stable IPv6 Interface Identifier allows for network activity to be correlated within the context in which the Interface Identifier is stable [RFC7721]. A stable-per-network IPv6 Interface Identifier (as in [RFC7217]) allows for network activity correlation within a network, whereas a constant IPv6 Interface Identifier (that remains constant across networks) allows not only network activity correlation within the same network, but also across networks ("host tracking").¶
Similarly, an implementation that generates TCP ISNs with a global counter could allow for fingerprinting and network activity correlation across networks, since an attacker could passively infer the identity of the victim based on the TCP ISNs employed for subsequent communication instances. Similarly, an implementation that generates predictable IPv6 Fragment Identification values could be subject to fingerprinting attacks (see e.g. [Bellovin2002]).¶
Transient numeric identifiers that result in specific patterns can produce an information leakage to other communicating entities. For example, it is common to generate transient numeric identifiers with an algorithm such as:¶
ID = offset(CONTEXT) + mono(CONTEXT);¶
This generic expression generates identifiers by adding a monotonically-increasing function (e.g. linear) to a randomized offset. offset() is constant within a given context, whereas mono() produces a monotonically-increasing sequence for the given context. Identifiers generated with this expression will generally be predictable within CONTEXT.¶
The predictability of mono(), irrespective of the predictability of offset(), can leak information that may be of use to attackers. For example, a node that selects ephemeral port numbers as in:¶
ephemeral_port = offset(Dest_IP) + mono()¶
that is, with a per-destination offset, but a global mono() function (e.g., a global counter), will leak information about total number of outgoing connections that have been issued by the vulnerable implementation.¶
Similarly, a node that generates Fragment Identification values as in:¶
Frag_ID = offset(IP_src_addr, IP_dst_addr) + mono()¶
will leak out information about the total number of fragmented packets that have been transmitted by the vulnerable implementation. The vulnerabilities described in¶
[Sanfilippo1998a], [Sanfilippo1998b], and [Sanfilippo1999] are all associated with the use of a global mono() function (i.e., with a global and constant "context") -- particularly when it is a linear function (constant increments of 1).¶
Predicting transient numeric identifiers can be of help for other types of attacks. For example, predictable TCP ISNs can open the door to trivial connection-reset and data injection attacks (see Section 8.6).¶
Fingerprinting is the capability of an attacker to identify or re-identify a visiting user, user agent or device via configuration settings or other observable characteristics. Observable protocol objects and characteristics can be employed to identify/re-identify a variety of entities, ranging from the underlying hardware or Operating System (vendor, type and version), to the user itself (i.e. his/her identity). [EFF] illustrates web browser-based fingerprinting, but similar techniques can be applied at other layers and protocols, whether alternatively or in conjunction with it.¶
Transient numeric identifiers are one of the observable protocol components that could be leveraged for fingerprinting purposes. That is, an attacker could sample transient numeric identifiers to infer the algorithm (and its associated parameters, if any) for generating such identifiers, possibly revealing the underlying Operating System (OS) vendor, type, and version. This information could possibly be further leveraged in conjunction with other fingerprinting techniques and sources.¶
Evasion of protocol-stack fingerprinting can prove to be a very difficult task: most systems make use of a wide variety of protocols, each of which have a large number of parameters that can be set to arbitrary values or generated with a variety of algorithms with multiple parameters.¶
Algorithms that, from the perspective of an observer (e.g., the legitimate communicating peer), result in specific values or patterns, will allow for at least some level of fingerprinting. For example, the algorithm from Section 7.3 will typically allow fingerprinting within the context where the resulting identifiers are stable. Similarly, the algorithms from Section 7.4 will result in a monotonically-increasing sequences within a given context, thus allowing for at least some level of fingerprinting (when the other communicating entity can correlate different sampled identifiers as belonging to the same monotonically-increasing sequence).¶
Thus, where possible, algorithms from Section 7.1 should be preferred over algorithms that result in specific values or patterns.¶
Identifiers that are not semantically opaque tend to be more predictable than semantically-opaque identifiers. For example, a MAC address contains an OUI (Organizationally-Unique Identifier) which identifies the vendor that manufactured the corresponding network interface card. This can be leveraged by an attacker trying to "guess" MAC addresses, who has some knowledge about the possible NIC vendor.¶
[RFC7707] discusses a number of techniques to reduce the search space when performing IPv6 address-scanning attacks by leveraging the semantics of the IIDs produced by traditional SLAAC algorithms (eventually replaced by [RFC7217]) that embed MAC addresses in the IID of IPv6 addresses.¶
In many cases, the collision of transient network identifiers can have a hard failure severity (or result in a hard failure severity if an attacker can cause multiple collisions deterministically, one after another). For example, predictable Fragment Identification values open the door to Denial of Service (DoS) attacks (see e.g. [RFC5722].).¶
Some protocols rely on "sequence numbers" for the validation of incoming packets. For example, TCP employs sequence numbers for reassembling TCP segments, while IPv4 and IPv6 employ Fragment Identification values for reassembling IPv4 and IPv6 fragments (respectively). Lacking built-in cryptographic mechanisms for validating packets, these protocols are therefore vulnerable to on-path data (see e.g. [Joncheray1995]) and/or control-information (see e.g. [RFC4953] and [RFC5927]) injection attacks. The extent to which these protocols may resist off-path (i.e. "blind") injection attacks depends on whether the associated "sequence numbers" are predictable, and effort required to successfully predict a valid "sequence number" (see e.g. [RFC4953] and [RFC5927]).¶
We note that the use of unpredictable "sequence numbers" is a completely-ineffective mitigation for on-path injection attacks, and also a mostly-ineffective mitigation for off-path (i.e. "blind") injection attacks. However, many legacy protocols (such as TCP) do not natively incorporate cryptographic mitigations, but rather only as optional features (see e.g. [RFC5925]), if at all available. Additionally, ad-hoc use of cryptographic mitigations might not be sufficient to relieve a protocol implementation of generating appropriate transient numeric identifiers. For example, use of the Transport Layer Security (TLS) protocol [RFC8446] with TCP will protect the application protocol, but will not help to mitigate e.g. TCP-based connection-reset attacks (see e.g. [RFC4953]). Similarly, use of SEcure Neighbor Discovery (SEND) [RFC3971] will still imply reliance on the successful reassembly of IPv6 fragments in those cases where SEND packets do not fit into the link Maximum Transmission Unit (MTU) (see [RFC6980]).¶
A number of algorithms discussed in this document (such as those described in Section 7.4.2 and Section 7.4.3) rely on PRFs. Implementations that employ weak PRFs or keys of inappropriate size can be subject to cryptanalysis, where an attacker can obtain the secret key employed for the PRF, predict numeric identifiers, etc.¶
Furthermore, an implementation that overloads the semantics of the secret key can result in more trivial cryptanalysis, possibly resulting in the leakage of the value employed for the secret key.¶
The following subsections analyze possible vulnerabilities associated with the algorithms described in Section 7.¶
Possible vulnerabilities associated with the algorithms from Section 7.1 include:¶
Where available, CSPRNGs should be preferred over regular PRNGs such as e.g. POSIX random(3) implementations. In scenarios where a CSPRNG is not readily available, a security and privacy assessment of employing a regular PRNG should be performed, supporting the implementation decision.¶
When employing a PRNG, many implementations "adapt" the length of its output with a modulo operator (e.g., C language's "%"), possibly changing the distribution of the output of the PRNG.¶
For example, consider an implementation that employs the following code:¶
id = random() % 50000;¶
This example implementation means to obtain a transient numeric identifier in the range 0-49999. If random() produces e.g. a pseudorandom number of 16 bits (with uniform distribution), the selected transient numeric identifier will have a non-uniform distribution with the numbers in the range 0-15535 having double-frequency than the numbers in the range 15536-49999.¶
This effect is reduced if the PRNG produces an output that is much longer than the length implied by the modulo operation. We note that to preserve a uniform distribution, the rejection sampling technique [Romailler2020] can be used.¶
Use of algorithms other than PRNGs for generating identifiers of this category is discouraged.¶
As noted in Section 7.2, this category can employ the same algorithms as Category #4, since a monotonically-increasing sequence tends to minimize the transient numeric identifier reuse frequency. Therefore, the vulnerability analysis in Section 9.4 also applies to this category.¶
Additionally, as noted in Section 7.2, some transient numeric identifiers of this category might be able to use the algorithms from Section 7.1, in which case the same considerations as in Section 9.1 would apply.¶
Possible vulnerabilities associated with the algorithms from Section 7.3 are:¶
The algorithm described in Section 7.4.1 for generating identifiers of Category #4 will result in an identifiable pattern (i.e. a monotonically-increasing sequence) for the transient numeric identifiers generated for each CONTEXT, and thus will allow for fingerprinting and network activity correlation within each CONTEXT.¶
On the other hand, a simple way to generalize and analyze the algorithms described in Section 7.4.2 and Section 7.4.3 for generating identifiers of Category #4, is as follows:¶
/* Transient Numeric ID selection function */ id_range = max_id - min_id + 1; retry = id_range; id_inc = increment() % id_range; do { update_mono(CONTEXT, id_inc); next_id = min_id + (offset(CONTEXT) + \ mono(CONTEXT)) % id_range; if (suitable_id(next_id)) { return next_id; } retry = retry - id_inc; } while (retry > 0); return ERROR;¶
Essentially, an identifier (next_id) is generated by adding a monotonically-increasing function (mono()) to an offset value, unknown to the attacker and stable for given context (CONTEXT).¶
The following aspects of the algorithm should be considered:¶
Considering the generic algorithm illustrated above, we can identify the following possible vulnerabilities:¶
This document has no IANA actions.¶
The entire document is about the security and privacy implications of transient numeric identifiers. [I-D.gont-numeric-ids-sec-considerations] recommends that protocol specifications specify the interoperability requirements of their transient numeric identifiers, perform a vulnerability assessment of their transient numeric identifiers, and suggest an algorithm for generating each of their transient numeric identifiers. This document analyzes possible algorithms (and their implications) that could be employed to comply with the interoperability properties of most common categories of transient numeric identifiers, while minimizing the associated negative security and privacy implications.¶
The authors would like to thank (in alphabetical order) Bernard Aboba, Jean-Philippe Aumasson, Steven Bellovin, Luis Leon Cardenas Graide, Spencer Dawkins, Theo de Raadt, Guillermo Gont, Joseph Lorenzo Hall, Gre Norcie, Colin Perkins, Vincent Roca, Shivan Sahib, Rich Salz, Martin Thomson, and Michael Tuexen, for providing valuable comments on earlier versions of this document.¶
The authors would like to thank Shivan Sahib and Christopher Wood, for their guidance during the publication process of this document.¶
The authors would like to thank Jean-Philippe Aumasson and Mathew D. Green (John Hopkins University) for kindly answering a number of questions.¶
The authors would like to thank Diego Armando Maradona for his magic and inspiration.¶
The following subsections discuss algorithms and techniques with known negative security and privacy implications.¶
One of the most trivial ways to achieve uniqueness with a low identifier reuse frequency is to produce a linear sequence. This type of algorithm has been employed in the past to generate identifiers of Categories #1, #2, and #4 (please see Section 6 for an analysis of these categories).¶
For example, the following algorithm has been employed (see e.g. [Morris1985], [Shimomura1995], [Silbersack2005] and [CPNI-TCP]) in a number of operating systems for selecting IP fragment IDs, TCP ephemeral ports, etc.:¶
/* Initialization code */ next_id = min_id; id_inc= 1; /* Transient Numeric ID selection function */ id_range = max_id - min_id + 1; retry = id_range; do { if (next_id == max_id) { next_id = min_id; } else { next_id = next_id + id_inc; } if (suitable_id(next_id)) { return next_id; } retry--; } while (retry > 0); return ERROR;¶
For obvious reasons, this algorithm results in predictable sequences. Since a global counter is used to generate the transient numeric identifiers ("next_id" in the example above), an entity that learns one numeric identifier can infer past numeric identifiers and predict future values to be generated by the same algorithm. Since the value employed for the increments is known (such as "1" in this case), an attacker can sample two values, and learn the number of identifiers that have been were generated in-between the two sampled values. Furthermore, if the counter is initialized e.g. when the system its bootstrapped to some known value, the algorithm will leak additional information, such as the number of transmitted fragmented datagrams in the case of an IP ID generator [Sanfilippo1998a], or the system uptime in the case of TCP timestamps [TCPT-uptime].¶
This algorithm offers a middle ground between the algorithms that generate randomized transient numeric identifiers (such as those described in Section 7.1.1 and Section 7.1.2), and those that generate identifiers with a predictable monotonically-increasing function (see Appendix A.1).¶
/* Initialization code */ next_id = random(); /* Initialization value */ id_rinc = 500; /* Determines the trade-off */ /* Transient Numeric ID selection function */ id_range = max_id - min_id + 1; retry = id_range; do { /* Random increment */ id_inc = (random() % id_rinc) + 1; if ( (max_id - next_id) >= id_inc){ next_id = next_id + id_inc; } else { next_id = min_id + id_inc - (max_id - next_id); } if (suitable_id(next_id)) { return next_id; } retry = retry - id_inc; } while (retry > 0); return ERROR;¶
This algorithm aims at producing a global monotonically-increasing sequence of transient numeric identifiers, while avoiding the use of fixed increments, which would lead to trivially predictable sequences. The value "id_inc" allows for direct control of the trade-off between unpredictability and identifier reuse frequency. The smaller the value of "id_inc", the more similar this algorithm is to a predicable, global linear ID generation algorithm (as the one in Appendix A.1). The larger the value of "id_inc", the more similar this algorithm is to the algorithm described in Section 7.1.1 of this document.¶
When the identifiers wrap, there is a risk of collisions of transient numeric identifiers (i.e., identifier reuse). Therefore, "id_inc" should be selected according to the following criteria:¶
Clearly, these are competing goals, and the decision of which value of "id_inc" to use is a trade-off. Therefore, the value of "id_inc" is at times a configurable parameter so that system administrators can make the trade-off for themselves. We note that the alternative algorithms discussed throughout this document offer better interoperability, security and privacy properties than this algorithm, and hence implementation of this algorithm is discouraged.¶
Employing the same identifier across contexts in which stability is not required (i.e. overloading the semantics of transient numeric identifier) usually has negative security and privacy implications.¶
For example, in order to generate transient numeric identifiers of Category #2 or Category #3, an implementation or specification might be tempted to employ a source for the numeric identifiers which is known to provide unique values, but that may also be predictable or leak information related to the entity generating the identifier. This technique has been employed in the past for e.g. generating IPv6 IIDs by re-using the MAC address of the underlying network interface. However, as noted in [RFC7721] and [RFC7707], embedding link-layer addresses in IPv6 IIDs not only results in predictable values, but also leaks information about the manufacturer of the underlying network interface card, allows for network activity correlation, and makes address-based scanning attacks feasible.¶