Within the Node.js ecosystem, a vulnerability has been unearthed pertaining to the foundational logic of the HTTP client, empowering threat actors to circumvent preexisting defenses against request splitting. Martino Spagnolo, operating under the moniker r3verii, promulgated a comprehensive analysis following the Node.js core team’s refusal to classify the issue as a transgression of their threat model. This concerns a mechanism that unlocks the perilous capability to inject headers and forge a secondary request within the confines of a singular connection.
The genesis of this predicament traces back to 2018, an era when the CVE-2018-12116 vulnerability permitted the infusion of control characters by exploiting the idiosyncrasies of the latin1 encoding. In response, the architects instituted a validation protocol for the request path within http.request, strictly prohibiting any characters falling outside the \u0021—\u00ff spectrum. However, this safeguard was triggered solely at the precise moment of the ClientRequest object’s instantiation. Thereafter, the path property languished as a mundane, mutable field, utterly devoid of any supplementary validation.
Subsequent investigation revealed that should the clientRequest.path be altered after the request’s genesis, this critical validation is effectively bypassed. During the synthesis of the HTTP string, the _implicitHeader method blindly consumes the current value of the path without subjecting it to secondary filtration. Consequently, pernicious control sequences, specifically \r\n, are granted unhindered passage directly into the TCP stream.
The ramifications naturally diverge contingent upon the transmitted payload. In its most rudimentary manifestation, a malefactor might inject supplementary HTTP headers, clandestinely supplanting the Host or Authorization directives. A profoundly more severe scenario affords the adversary the capability to terminate the headers entirely and append a forged request body, thereby fundamentally altering the semantics of the original invocation. The most catastrophic permutation culminates in unequivocal HTTP request splitting, wherein the unsuspecting server receives two autonomous HTTP solicitations masquerading as one.
The researcher meticulously audited ubiquitous libraries, unmasking seven prominent projects plagued by this defenseless Time-of-Check to Time-of-Use (TOCTOU) chasm. Among these casualties are node-http-proxy, http-proxy-middleware, superagent, request, and @hapi/wreck. Their collective download volume exceeds a staggering 160 million per week. The architectural flaw remains uniformly consistent across the board: the library instantiates the ClientRequest, then prematurely relinquishes the object to external handlers prior to the dispatch of the headers. If a handler subsequently modifies the path predicated on user-supplied data, the filtration mechanism is completely circumvented.
Certain utilities have serendipitously evaded this peril by virtue of their divergent architectures. axios leverages follow-redirects and strictly withholds access to the internal ClientRequest; got dispatches the headers preceding any external exposure of the object; while undici and the native fetch API employ bespoke implementations of the HTTP stack entirely bereft of a mutable path property.
Communicating via the HackerOne program, the Node.js vanguard maintained that this behavior aligns perfectly with their current operational logic, refusing to acknowledge it as a legitimate vulnerability. From their vantage point, the onus of responsibility rests squarely upon the external libraries that permit the alteration of the path post-instantiation. The architect of this research vehemently dissented with this appraisal, underscoring the colossal scale of the vulnerable ecosystem and parading fully functional proofs of concept.
As a rudimentary panacea, it is proposed that the path undergo a secondary validation immediately prior to the synthesis of the request string, or alternatively, that the path property be fortified via a setter equipped with stringent validation logic. Whilst no core modifications are presently anticipated, developers are strongly exhorted to meticulously scour their codebases for any assignments to clientRequest.path, rigorously verifying that these values are never derived from unsanitized user input.