Github  Printable

Threat environment

The threat environment for Node.js is similar to that for other runtimes that are primarily used for microservices and web frontends, but there are some Node.js specific concerns.

We define both kinds of threats in this section. A reader familiar with web-application security can skip all but this page and the discussion of unintended require without missing much, but may find it helpful to refer back to the table below when reading later chapters.

Server vs Client-side JavaScript

Before we discuss the threat environment, it's worth noting that the threat environment for server-side JavaScript is quite different from that for client-side JavaScript. For example,

  • Client-side JavaScript runs in the context of the same-origin policy possibly with a Content-Security-Policy which governs which code can load. Server-side JavaScript code loading is typically only constrained by the files on the server, and the values that can reach require(...), eval(...) and similar operators.
  • Client-side JavaScript typically only has access to data that the human using the browser should have access to. On the server, applications are responsible for data compartmentalization, and server-side JavaScript often has privileged access to storage systems and other backends.
  • File-system access by the client typically either requires human interaction (<input type=file>, Content-disposition:attachment), or can only access a directory dedicated to third-party content (browser cache, local storage) and which is not usually on a list like $PATH. On the server, the Node runtime process's privileges determine file-system access.
  • Client-side JavaScript has no concept of a shell that converts strings into commands that runs outside the JavaScript engine. Server-side JavaScript can spawn child processes that operate on data received over the network, and on data that is accessible to the Node runtime process.
  • Network messages sent by server-side JavaScript originate inside the server's LAN, but those sent by client-side JavaScript typically do not.
  • Shared memory concurrency in client-side JavaScript happens via well-understood APIs like SharedArrayBuffer. Experimental modules (code) and a workers proposal allow server-side JavaScript to fork threads; it is unclear how widespread these are in production or how susceptible these are to memory corruption or exploitable race conditions.

The threat environment for server-side JavaScript is much closer to that for any other server-side framework than JavaScript in the browser.

Classes of Threats

The table below lists broad classes of vulnerabilities, and for each, a short identifier by which we refer to the class later in this document. This list is not meant to be comprehensive, but we expect that a thorough security assessment would touch on most of these and would have low confidence in an assessment that skips many.

The frequency and severity of vulnerabilities are guesstimates since we have little hard data on the frequency of these in Node.js applications, so have extrapolated from similar systems. For example, see discussion about frequency in buffer overflow.

For each, relevant mitigation strategies appear in the mitigations columns, and link to the discussion.

Shorthand Description Frequency Severity Mitigations
0DY Zero-day. Attackers exploit a vulnerability before a fix is available. Low-Med Med-High cdeps fail
BOF Buffer overflow. Low High ovrsi
CRY Misuse of crypto leads to poor access-control decisions or data leaks. Medium Medium ovrsi
DEX Poor developer experience slows or prevents release of features. ? ? dynam ovrsi
EXF Exfiltration of data, e.g. by exploiting reflection to serialize more than intended. Med-High Low-Med ovrsi
LQC Using low quality dependencies leads to exploit Medium Low-Med kdeps ovrsi
MTP Theft of commit rights or MITM causes npm install to fetch malicious code. Low Med-High kdeps cdeps
QUI Query injection on a production machine. Medium Med-High ovrsi qlang
RCE Remote code execution, e.g. via eval Med-High High dynam ovrsi
SHP Shell injection on a production machine. Low High ovrsi cproc
UIR require(untrustworthyInput) loads code not intended for production. Low Low-High dynam

Meltdown and Spectre

As of this writing, the security community is trying to digest the implications of Meltdown and Spectre. The Node.js blog addresses them from a Node.js perspective, so we do not comment in depth.

It is worth noting though that those vulnerabilities lead to breaches of confidentiality. While confidentiality violations are serious, the suggestions that follow use design principles that prevent a violation of confidentiality from causing a violation of integrity. Specifically:

  • Knowing a whitelist of production source hashes does not allow an attacker to cause a non-production source to load.
  • Our runtime eval mitigation relies on JavaScript reference equality, not knowledge of a secret.

results matching ""

    No results matching ""