Nowadays, web services are constantly undergoing a variety of attacks. Therefore, security is something that should be remembered at all stages of the project life cycle. The authors of the material, the translation of which we are publishing today, support the
repository on GitHub, which contains about 80 recommendations for securing applications running on the Node.js platform. This material, which was based on many security publications, collected more than two dozen recommendations related to Node.js, and some general tips. At the same time, this material covers the top 10 vulnerabilities from the OWASP project list.

1. Use the rules for the linter aimed at checking the security code
Recommendations
During development, use a security-oriented
linter plugin , such as
eslint-plugin-security . This allows you to identify vulnerabilities and security issues very early - at the time of writing the appropriate code. This approach helps to find weak spots in program security. Among them is the use of the
eval
command, a call to child processes, the import of modules with the transfer to the appropriate command of something different from a string literal (say, a certain string formed on the basis of data transmitted to the server by the user).
β
Here is a useful material about the linter rules.
')
Adam Baldwin speaks about linting as follows: βLinter should not be just a tool that meticulously follows the application of the rules regarding the number of spaces used, the semicolon distribution and the use of the eval command. ESLint gives the developer a powerful platform that allows you to detect a wide range of potentially dangerous patterns in the code and eliminate them. We are talking, for example, about regular expressions, about checking user input, and so on. I believe that the linter gives developers who are concerned about security issues a new, powerful tool that needs attention. β
Possible problems
What looks like a minor security bug during development is a serious vulnerability in production. In addition, if all project developers do not follow uniform security rules when working with code, this may lead to the appearance of vulnerabilities in it, or, for example, to confidential data in public repositories.
2. Limit the number of simultaneous requests to the server
Recommendations
DoS-attacks are very popular among attackers, it is relatively easy to conduct them. You can implement a system for limiting the number of requests to an application using an external service, such as a cloud load balancer, cloud firewall, nginx server, or, for small applications that are not critical, using middleware to limit the number of requests like
express-rate -limitβ
Here is the material on the implementation of the system for limiting the frequency of requests to the server
Possible problems
An application that does not have a system for limiting the number of simultaneous requests may be attacked, which will lead to its failure. This is reflected in the fact that users of such an application will either have difficulty working with it, or will not be able to interact with it at all.
3. Remove confidential information from configuration files or encrypt it.
Recommendations
Never store sensitive data in plain text in configuration files or in code. Instead, use confidential data management systems like Vault products or Kubernetes / Docker Secrets systems, or use environment variables to store such data. Confidential data stored in the version control system must be encrypted, measures must be taken to securely store and use it. Such measures include the use of dynamic keys, the use of password expiration dates, security audits, and so on. Use systems to check the code before committing or before sending it to the repository to prevent confidential data from being sent to the public repository by accident.
β
Here is a material about managing sensitive data.
Possible problems
Even if the code is stored in a closed repository, one day, by mistake, it may become publicly available. At this moment, all the confidential data stored in it will become public domain. As a result, the access of third parties to the repository with the code will inadvertently lead to the fact that they will get access to the systems associated with it (databases, API, services, and so on).
4. Prevent code injection vulnerabilities.
Recommendations
In order to prevent SQL / NoSQL injections and other similar attacks, always use ORM / ODM libraries or DBMS mechanisms aimed at clearing data, or supporting named or indexed parameterized queries that check what comes from the user. Never use, for the introduction of certain values ββin the texts of the requests, only javascript string strings, or string concatenation, since this approach opens your application to a wide range of vulnerabilities. All reputable Node.js libraries used to work with data (for example,
Sequelize ,
Knex ,
mongoose ) contain built-in protection against attacks by
injecting code.
β
Here is a material on the prevention of injections using ORM / ODM libraries.
Possible problems
Using untested and unclean data received from the user in queries can lead to an attack by introducing an operator when working with a NoSQL database like MongoDB. If you do not use a data cleaning system or an ORM library when working with a SQL database, this will lead to the possibility of an attack by SQL injection, which creates a huge gap in the application's security system.
5. Dodge DoS attacks by explicitly specifying the abnormal termination conditions of the process.
Recommendations
The Node process crashes when an unhandled error occurs. But in many of the recommendations reflecting the best development practices for Node, it is recommended that processes be completed even when the resulting error was intercepted and processed. Express, for example, will crash if any asynchronous error occurs - unless the routes are wrapped in
catch
expressions. This fact opens up a very attractive opportunity to attackers. They, having found out that at receipt of a certain request the process is terminated abnormally, they start to send him just such requests. There is no recommendation that would allow to solve this problem in one fell swoop, however, some techniques can soften it. So, at the end of the process due to an unhandled error, you need to notify the administrator, giving such notification the highest priority priority. It is necessary to check what comes to the process in the requests and to avoid situations of a process crash due to requests that are accidentally or intentionally formed incorrectly. All routes need to be wrapped in
catch
expressions and set up the system so that if the cause of the error is a query, the process would not crash (as opposed to what happens at the global application level).
Possible problems
Let's analyze the following situation. There are many Node.js applications. What happens if we start sending them POST requests with empty JSON as the request body? This will cause many of these applications to crash.
Now, if we played the role of intruders, we, in order for applications to continue to fail, it would be enough to continue to send them similar requests.
6. Configure HTTP response headers to enhance project security.
Recommendations
The application should use security-oriented HTTP headers to prevent attackers from resorting to such common attack techniques as cross-site scripting (XSS), clickjacking, and others. Customizing the headers is easy using special modules such as
helmet .
β
Here is a story about using secure headers.
Possible problems
If secure HTTP headers are not used, attackers will be able to carry out attacks on users of your applications, which leads to huge vulnerabilities.
7. Constantly, automatically, check your projects for the use of vulnerable dependencies in them.
Recommendations
In the NPM ecosystem, projects with many dependencies are a very common phenomenon. Dependencies should always be monitored, given the discovery of new vulnerabilities. Use tools like
npm audit ,
nsp, or
snyk to detect, monitor, and fix vulnerable dependencies. Embed these tools into your continuous integration system. This will allow you to detect vulnerable dependencies before they fall into production.
β
Here is a material about project dependency security
Possible problems
An attacker can identify the web framework used in the project and carry out attacks on all of its known vulnerabilities.
8. Try not to use the standard Node.js crypto module for password handling, use Bcrypt instead.
Recommendations
Passwords or other sensitive data (API keys, for example) need to be stored, processing them with cryptographic functions using βsaltβ, such as Bcrypt. It is worth using something similar, and not the standard Node.js
crypto
module for security and performance reasons.
β
Here is the Bcrypt material
Possible problems
Passwords or some confidential data that is stored without the use of appropriate measures to protect them are vulnerable to brute force attacks and dictionary attacks, which ultimately lead to the disclosure of such data.
9. Use character shielding systems in HTML, JS and CSS data sent to the user.
Recommendations
If some data from an untrusted source is sent to the user's browser, then even if it should just be displayed on the screen, such data can be a code that can be executed. This is commonly referred to as cross-site scripting (XSS). The risk of such attacks can be reduced by using special libraries that process data so that they cannot be executed. This is called data encoding or escaping.
β
Here is the screening of the output.
Possible problems
If you do not care about data shielding, an attacker may, for example, save malicious JavaScript code in your database, which can be transmitted to clients unchanged and run.
10. Check JSON data coming to server
Recommendations
Monitor the contents of the bodies of incoming requests, checking whether they correspond to what you expect to see in such requests. If the request does not look as expected, quickly stop processing it. To avoid the cumbersome operation of writing request verification code for each route, you can use lightweight JSON tools to validate data, such as
jsonschema or
joi .
β
Here is a material about checking incoming JSON data
Possible problems
If the server cordially accepts any requests without thoroughly checking them, this greatly increases the attack surface of the application and inspires attackers to try out a lot of requests to find those that cause the system to crash.
11. Maintain black lists of JWT tokens.
Recommendations
When using JWT tokens (for example, if you work with
Passport.js ), by default, there is no standard mechanism for revoking system access privileges for already issued tokens. Even if you find that a user is doing something obviously abnormal, you do not have a way, through the mechanism of tokens, to close his access to the system, as long as he has a valid token. This problem can be mitigated by implementing a black list of untrusted tokens, which are validated upon each request.
β
Here is a material about blacklists of JWT tokens
Possible problems
Tokens in the wrong hands can be used by an attacker. He can access the application and impersonate a regular user - the owner of the token.
12. Limit the number of login attempts.
Recommendations
In applications based on express, to protect against attacks by brute force and dictionary attacks, you should use the appropriate middleware, such as
express-brute . Similarly, you need to protect very important routes, such as
/admin
or
/login
. Protection should be based on analyzing the properties of the request, such as the user name used in the request or other identifiers, such as parameters of the request body.
β
Here is a material on limiting the number of login attempts.
Possible problems
If the application does not limit the number of login attempts, then the attacker can, in automatic mode, send your system an unlimited number of login requests, for example, trying to gain access to a privileged account.
13. Run Node.js on behalf of a user who does not have root privileges.
Recommendations
An extremely common scenario is that when using Node.js, it runs as a root user with unlimited privileges. For example, this is how everything is configured by default in Docker containers. It is recommended that you create a user who does not have root-rights, and either embed it in the Docker image, or start the process on behalf of this user by calling the container with the
-u username
flag.
β
Here is the material on running Node.js on behalf of a non-root user.
Possible problems
If Node.js is running as a root user, then an attacker who was able to run a script on the server gets unlimited possibilities on the local machine. Let's say it can change the
iptable
settings and redirect traffic to its own computer.
14. Limit the amount of data transmitted in the requests, using a reverse proxy server or middleware
Recommendations
The larger the amount of data in the request body, the more difficult it is for a single-threaded server to process such a request. Using large requests allows an attacker to overwhelm the server with unnecessary work and without sending him a huge number of requests (that is, without performing a DoS / DDoS attack). You can reduce the risk of such attacks by limiting the size of the bodies of incoming requests on a certain border system (on a firewall or load balancer), or by setting
body-parser express to receive only packets containing a small amount of data.
β
Here is a material about limiting the amount of data transmitted in requests
Possible problems
If you do not limit the amount of data transmitted in the requests, the attacker can download the application to handle large requests. At this time, it will not be able to solve the tasks for which it is designed. This leads to a drop in performance and makes the application vulnerable to DoS attacks.
15. Avoid using the eval function in JavaScript
Recommendations
The
eval
function is evil because it allows you to execute arbitrary JS code passed to it during program execution. Moreover, the discussion here is not only about the fact that this code can slow down the operation of the application. This feature is a serious security threat, as it can get malicious JS-code sent to the server by an attacker.
In addition, the
new Function
constructor should be avoided. The
setTimeout
and
setInterval
functions never need to pass dynamically generated JS code.
β
Here is the material about eval
Possible problems
If someone finds a way to transmit malicious JS code, in the form of text,
eval
function, or some other similar JS mechanism, he will have full access to the page, to all that can be done using JavaScript. This vulnerability is often associated with XSS attacks.
16. Do not overload single-threaded Node.js applications with malicious regular expressions.
Recommendations
Regular expressions, while convenient, are a threat to JavaScript applications in general, and in particular to the Node.js platform. Processing using a regular expression of what came from the user can create a huge load on the processor. For example, regular expression processing can be so inefficient that checking ten words can block an event cycle for a few seconds and overload the processor. Therefore, it is best to use third-party packages for checking string data, like
validator.js , instead of writing your own regular expressions. You can use the
safe-regex package to detect vulnerable regular expression patterns.
β
Here is a material on the fight against malicious regular expressions.
Possible problems
Badly written regular expressions may be subject to a special kind of DoS attacks, during which the cycle of events is completely blocked. For example, in November 2017 it was discovered that the popular package
moment
is vulnerable to similar attacks.
17. Avoid loading modules using variables.
Recommendations
Avoid importing files, the path to which is specified as a parameter, based on the considerations according to which this parameter can be set on the basis of user input. This rule can be extended by including here and, in general, access to files (using
fs.readFile()
), and access to any other important resources using parameters obtained from the user. Using
eslint-plugin-security allows you to detect such insecure patterns very early.
β
Here is a material about safe loading of modules.
Possible problems
The data sent to the server by the attacker may be in the parameters that are responsible for importing some files, among which there may be a malicious file uploaded to the server in advance. This data can also be used to access the system files already on the computer.
18. Run unsafe code in the sandbox.
Recommendations
, (, ), «», . , (
cluster.fork()
), npm-, .
β
, , . β , , , .
19.
Recommendations
, , . , , , .
child_process.execFile
, , , , , .
β
,
, , , , .
20.
Recommendations
express, , . , , ,
Error
( ). , , , - , .
β
, , , , , , .
21. npm Yarn
Recommendations
, -, (MFA, multi-factor authentication). npm Yarn , . , , , . , . , , npm, .
, ?
eslint, .
22. ,
Recommendations
- . , β , - . , ,
X-Powered-By
, , . , ( , , Node.js express).
β
-
- . , , -, β . .
23.
, . β , Node.js.
,
OWASP .
- root- .
- ( SSH-).
- , , , . OWASP, .
- , . , .
- OAuth, OpenID, . Basic Authentication.
- . , ( β ), X , Y .
- β , β . , .
- , , . ( β GitHub, AWS, Jenkins, ).
Results
. , Node.js-.
Dear readers! -, ?
