Quite a lot has already been said about the popularity of NodeJS. The increase in the number of applications is obvious – NodeJS is quite easy to learn, it has a huge number of libraries, as well as a dynamically developing ecosystem.

We made recommendations for NodeJS developers based on OWASP Cheat Sheets to help you anticipate security issues when developing applications.

Security recommendations for NodeJS applications can be divided into the following categories:

  • Security during application development;
  • Server security;
  • Platform security;

Application Development Security

Avoid callback hell

Using callback functions (callbacks) is one of NodeJS’s greatest strengths, however, when nesting callbacks, you can easily forget to handle the error in one of the functions. One way to avoid callback hell is to use promises. Even if the module you are using does not support working with promises, you can always use Promise.promisifyAll (). But even using promises, it is worth paying attention to nesting. To completely avoid the callback hell error, stick to a “flat” chain of promises.

Callback hell example:

The same code using a flat chain of promises:

Limit the size of request

Parsing the request body can be quite a resource-intensive operation. If you do not limit the size of the request, attackers will be able to send large enough requests that can fill up all disk space or exhaust all server resources, but at the same time, limiting the request size for all cases may be incorrect, because there are requests, such as downloading a file. Therefore, it is recommended to set limits for different types of content. For example, using the express framework, this can be implemented as follows:

It should be noted that an attacker can change the type of request content and circumvent restrictions, therefore, it is necessary to check whether the content of the request matches the type of content specified in the request header. If checking the type of content affects performance, you can only check certain types or queries that are larger than a certain size.

Do not block event loop

An important component of the language is the event loop, which just allows you to switch the execution context without waiting for the operation to complete. However, there are blocking operations whose completion NodeJS has to wait before continuing with the code. For example, most synchronous methods are blocking:

It is recommended to perform such operations asynchronously:

At the same time, do not forget that the code which comes after the asynchronous call will be executed without waiting for the completion of the previous operation.

For example, in the code below, the file will be deleted before it is read, which can lead to a race condition.

To avoid this, you can write all operations in a non-blocking function:

Check input fields

Checking input fields is an important part of the security of any application. Validation errors can cause your application to become vulnerable immediately to many types of attacks: sql injection, xss, command injection, and others. To simplify form validation, you can use the validator packages, mongo-express-sanitize.

Shield user data

One of the rules, the implementation of which will help you protect yourself from xss attacks, is to shield user data. You can use the escape-html or node-esapi library for this.

Keep logs

In addition to helping to debug errors, logging can be used to respond to incidents. One of the most popular NodeJS logging packages is Winston and Bunyan. The example below shows how to use Winston to output logs to both the console and the file:

Control the event loop

If your server is in conditions of intensive network traffic, users may experience difficulties with the availability of your service. This is essentially a DoS attack. In this case, you can monitor the response time and, if it exceeds the specified time, send a message – 503 Server Too Busy. The toobusy-js module can help.

An example of using the module:

Take precautions against brute force

Again modules come to the rescue. For example, express-brute or express-bouncer. Usage example:

Using CAPTCHA is another common brute force countermeasure. A frequently used module to help implement CAPTCHA is svg-captcha.

Use CSRF Tokens

One of the most reliable ways to protect against CSRF attacks is to use a CSRF token. The token must be generated with high entropy, strictly checked and be connected to the user’s session. To ensure the operation of the CSRF token, you can use the csurf module.

Usage example:

Do not forget to add the token to the hidden field on the page:

Delete unnecessary routes

The web application should not contain pages that are not used by users, as this can increase the attack surface. Therefore, all unused API routes must be disabled. You should especially pay attention to this question if you use Sails or Feathers frameworks, since they automatically generate API endpoints.

Protect yourself from HPP (HTTP Parameter Pollution)

By default, express adds all the parameters from the request to an array. OWASP recommends using the hpp module, which ignores all parameter values ​​from req.query and / or req.body and simply selects the last value from among the repeated ones.

Monitor return values

For example, the user table can store important data: password, email address, date of birth, etc. Therefore, it is important to return only the necessary data.

For example:

Use descriptors

Using descriptors, you can describe the behavior of a property for various operations: writable – whether it is possible to change the value of a property, enumerable – whether it is possible to use a property in a for..in loop, configurable – whether it is possible to overwrite a property. It is recommended to pay attention to the listed properties, since when defining the property of an object, all these attributes are set to true by default. You can change the value of properties as follows:

Use ACL

The acl module can help to differentiate data access based on roles. For example, adding permission looks like this:

Catch uncaughtException

By default, in the case of an uncaught exception, NodeJS will print the current stack trace and terminate the execution thread. However, NodeJS allows you to customize this behavior. In case of an uncaught exception, an uncaughtException event is raised, which can be caught using the process object:

It is worth remembering that when an uncaughtException occurs, you must clear all allocated resources (for example, file descriptors and handlers) before completing the Z process in order to avoid unforeseen errors. It is strongly discouraged to continue the program if an uncaughtException occurs.

Also, when displaying error messages, the user should not disclose detailed error information, such as stack trace.

Server security

Set flags for headers when working with cookies

There are several flags that can help to protect against attacks such as xss and csrf: httpOnly, which prevents access to cookies through javascript; Secure – allows sending cookies only via HTTPS and SameSite, which determines the ability to transfer cookies to a third-party resource.

Usage example:

Set HTTP Headers for Security

Below are the headers and examples of their connection, which will help you protect yourself from a number of common attacks. Headers are set using the helmet module

• Strict-Transport-Security: HTTP Strict Transport Security (HSTS) tells the browser that the application can only be accessed via HTTPS

• X-Frame-Options: determines whether the page can be used in frame, iframe, embed or object

• X-XSS-Protection: Allows the browser to stop loading the page if it detects an XSS reflected attack

• X-Content-Type-Options: used to prevent attacks using MIME types

• Content-Security-Policy: Prevents attacks such as XSS and data injection attacks.

• Cache-Control and Pragma: for managing caching, especially this header can be useful for pages that contain sensitive data. However, remember that disabling caching on all pages can affect performance.

• X-Download-Options: header prevents Inter Explorer from executing downloaded files

• Expect-CT: Certificate Transparency – a mechanism created to solve some problems with the infrastructure of SSL certificates, this header tells the browser about the need for additional certificate verification in CT logs

• X-Powered-By: An optional header that is used to indicate the technology used on the server. You can hide this header as follows:

In addition, you can change the value to hide real information about the technologies you use:

Platform security

Update your packages

The security of your application depends on the security of the packages you use, so it is important to use the latest version of the package. To make sure that the package you are using does not contain known vulnerabilities, you can use the special OWASP list. You can also use the library that checks packages for known vulnerabilities Retire.js.

Do not use unsafe functions

There are functions that are recommended to be discarded whenever possible. Among these functions is eval (), which executes the string taken as an argument. In combination with user input, using this function can lead to a remote code execution vulnerability, since for similar reasons, using child_process.exec is also unsafe, because the function passes the received arguments to bin / sh.

In addition, there are a number of modules that you should use with caution. For example, the fs module for working with files. If in a certain way the generated user input is passed into a function, then your application may become vulnerable to including a local file and directory traversal.

The vm module, which provides an API for compiling and running code on a V8 virtual machine, should be used only in the sandbox.

Be careful using regular expressions

A regular expression can be written so that you can achieve a situation where the runtime of the expression will grow exponentially, which can lead to a denial of service. Such attacks are called ReDoS. There are several tools to check if regular expressions are safe, one of which is vuln-regex-detector.

Run linter periodically

During development, it’s hard to keep all safety recommendations in mind, and when it comes to team development, it’s not easy to achieve compliance with the rules by all team members. For such purposes, there are tools for static security analysis. Such tools, without executing your code, look for vulnerabilities in it. In addition, linter allows you to add custom rules for finding places in the code that may be vulnerable. The most commonly used linters are ESLint and JSHint.

Use strict mode

Javascript has a number of insecure and obsolete features that should not be used. To exclude the possibility of using these functions, strict mode is also provided.

Adhere to general safety principles

The recommendations described focus on NodeJS, but do not forget about the general security principles that must be observed regardless of the platform used.