Beyond the success of Kotlin: a documentary about how and why Kotlin succeeded in the world of Android development.

Top 15 best practices for NodeJS security issues mitigation

Learn about Node.js security issues, best practices for avoiding these risks, and how to build secure Node.js applications.

5 stars and a hand on purple background

Introduction

Being the leader among web frameworks, Node.js is a secure platform. However, it uses many third-party packages, most of which are open-source. All these third-party elements are combined under the Node Package Manager (NPM) roof. And a significant part of them can be flawed and harm the security of the Node.js ecosystem.

According to the analysis of NPM published on GitHub, the share of vulnerable infrastructure elements was 14%. Since that analysis was issued back in 2017 the NPM ecosystem has grown significantly, and with it the capacity for more infrastructure vulnerabilities.

EngX AI-Supported Software Engineering
Integrate GitHub Copilot and ChatGPT into your daily work for streamlined, efficient development.
View coursearrow-right-blue.svg

The 2022 State of Open Source Report shows about 3 out of 4 web projects use open-source components. Node.js-based projects are no exception. So, the scale of the problem seems at least significant enough to demand further scrutiny, and likely warrants separate research.

In this article, we’ll identify the top security risks tied to Node.js and help Node.js developers avoid losses of revenue and reputation via adhering to the best Node.js security practices.

Node.js security vulnerabilities: how much could your business lose?

Potential harm from security breaches caused by the Node.js-related vulnerabilities could have both financial and non-financial repercussions. To understand the scope of possible losses, let’s look at the numbers.

  1. The 2022 IBM security report found a significant increase in the number of cyberattacks over the last few years, a trend that is currently continuing.
  2. In another report, IBM claims that the average total cost of a data breach for 2022 was $4.24 million globally and $9.44 million in the U.S. — and increasing year-over-year.
  3. Researchers from Comparitech analyzed a number of NASDAQ-listed companies and found that those that experienced data breaches show a 15%+ market underperformance after three years. This is a complex result of a negative reputational impact, increased churn rate, and other indirect factors.
  4. Speaking about downtimes, Atlassian calculates the cost of a minute of downtime for small businesses can reach as high as $427, and for medium- and large-sized businesses that number can climb to $9,000 for every 60 seconds lost.

As you can see, each single security vulnerability can potentially cause a Node.js security issue.

Top 8 Node.js security risks

As we have outlined how Node.js security issues are a big deal and can cost far too much, let’s put a spotlight on the most important risks tied to Node.js. Below you can find the top eight of them.

1. Brute force attacks

This type of cybersecurity attack is probably one of the oldest yet most actively used. The attacker is generating a list of “login:password” pairs and tries them in batches until one pair is successful. This success results in unauthorized access to sensitive data.

Preventing such attacks can be successful by reviewing the process of authentication. For example, including a limit of login attempts per period is enough in most cases.

2. Distributed denial of service (DDoS)

The DDoS attack is the most commercialized form of cybercrime globally. It is possible to order such an attack via the “Dark Web” and pay accordingly per its desired intensity and duration. As a result, the attacked server sticks in hordes of false requests, so much so the real ones can’t reach their destinations. Some Node.js versions open additional doors for DDoS due to known flaws.

There are various DDoS countermeasures in existence. They include both native and cloud-based special DDoS-related solutions, scaling up the infrastructure, limiting the number of requests per single IP, detecting and addressing unusual traffic patterns, and many others.

3. Cross-site scripting (XSS)

Technically, XSS makes it available for attackers to inject modified JavaScript code pieces at the client side. This ability is due to a lack of validation of hostnames returned by the DNS. The user’s browser receives some malicious code without the possibility of checking its legitimacy, then executes it, providing the attacker access to sensitive data like cookies, tokens, etc.

4. Cross-site request forgery (CSRF) attacks

This type of attack utilizes known flaws of the HTTP protocol. With a bit of social engineering (like sending a false link via email), the attacker forces the end user already logged in to act in a certain way.

The scale of possible harm usually depends on the role of the attack victim. If the victim has admin rights, the entire system is under threat. If the victim is just a regular user, the malicious actions are concentrated across their account — personal data, available funds, etc.

You can also check OWASP’s article about code-reviewing practices for avoiding CSRF.

5. Code injections

Most often, code injection attacks refer to insufficient validation of input and output data. The attacker finds any way possible to inject additional pieces of code into the conventional Node.js packages. These malicious pieces can be a kind of SQL commands (the most frequent), HTML scripts, injections into dynamic JS code, shell injections, and even the inclusion of third-party files.

The results from Node.js code injection security attacks may vary widely, from stealing unessential info to a fatal crash of the system.

6. Regular expression denial of service (REDoS) attacks

The REDoS attacks are an advanced and much smarter way to overload your servers than the usual DDoS. The desired effect is achieved via adding regular expressions that require complex computations that resemble usual requests to the server. As a result, there is no need for a huge number of such requests to down the attacked server.

Such attacks, among other things, require increasingly sophisticated countermeasures, those evolved beyond just limiting the number of requests per IP address. More thorough input validation is one of them.

7. Cookie poisoning

This is an entire family of attacks united under the flag of manipulating users’ cookies. In some circumstances, it is possible to scan and then modify the end user’s cookies in order to achieve different malicious results — session hijacking, spoofing, fixation, etc. In theory, sometimes it is enough just to change cookies manually in the browser and then reload the page to get an effect.

Preventing cookie poisoning is a developer’s crucial duty. Start by renaming all the cookies — this is done in order not to use the default names. This one step will surely stop attackers from finding the right cookies so easily.

8. X-Powered-By header

Using a non-standard HTTP response header helps attackers to get access to some sensitive information like the tech stack used within the application. This, in turn, creates an understanding of vulnerabilities that could be exploited to penetrate into the app.

The developer could easily disable the X-Powered-By header when configuring the server.

Node.js security best practices

Now it’s time to review the Node.js security best practices it’s essential to implement.

1. Allow using secure passwords only

Forbid the use of plain text passwords. Their problem isn’t just in the fact they can easily be cracked through brute force. Once you allow users to create plain text passwords, they start adding such passwords as 123456qwerty, asdfgh, strongpassword, AqSwDeFr, and so on. For experienced attackers, such passwords are what just the doctor ordered.

As you are likely aware (or should be), secure passwords must be at least 10 symbols long and contain both uppercase and lowercase letters, numbers, and special symbols.

2. Secure your stored passwords with encryption

A simple storing of your actual “login:password” pairs in the database is a very bad idea. The first crook retrieved information from your database will find and identify them during the very start of the analysis. In this case, no matter how strong the passwords were, all the accounts will turn out to be compromised.

Using reversible algorithms to encrypt your passwords before placing them into the database is a reliable protection measure — when equally reliable encryption methods are used, of course.

3. Limit the number of login attempts per user per unit of time

A brute-force attack cannot be performed with a limited login attempt per unit of time. Actually, hackers must go through a huge amount of options during the allotted time. For the real end users, there is absolutely no need for hundreds of login attempts per each minute that goes by. All such behavior is a clear sign of attack; that’s why a reasonable limitation is very helpful here.

4. Keep your Node.js and its packages up-to-date

The more time that has passed from the release of a certain version of software, the better all its flaws are known to the community using that software. That’s why outdated software so often acts as an open door for attacks. Update both your core Node.js as well as all its affiliate packages regularly to fit the security standards.

What about those open-source packages widely used within the Node.js ecosystem? Unfortunately, such projects can be created and powered primarily by enthusiasm, and quite often aren’t well-polished and/or even abandoned at some point. We don’t recommend using such abandoned packages in your projects at all.

5. Do not install questionable packages

As we said above, there are many abandoned packages within the Node.js NPM ecosystem. On top of that, there are many other shady packages, even those relatively new or fresh-sounding, that can be potentially quite harmful.

Always think twice before installing any third-party packages. Read user reviews, use the checking tools, and always opt for something less risky if you have an alternative available.

6. Always validate input and output data

A significant part of the risk described above is tied to a lack or improper validation of input and output data. In other words, with proper validation, there will be much fewer security risks, including most of the code injections, cross-site scripting, REDoS, and other attacks.

Validation should be implemented at both syntactic and semantic levels. Syntactic validation stands for correct syntax checking, whereas semantic validation checks the correctness of the values put into the input fields. You can learn more about input validation here.

7. Always replace default cookie names

Using the default cookie names is a mistake that is characteristic of newbie programmers. Experienced developers always change cookie names to make them less recognizable for attackers.

8. Outsource your cybersecurity

The nature of DDoS attacks is quite simple: force your server to process more requests than it is able to process. Some companies are very advanced in the prevention of such attacks. For example, Cloudflare offers cloud DDoS security for a fairly reasonable price. There are many other distinct cybersecurity-related questions you can successfully outsource.

9. Monitor your cybersecurity regularly

Conduct frequent and regular penetration testing activities to log and monitor the state of your cybersecurity. Such regularity and frequency often helps detect the vulnerability before it even becomes an issue, or recognizes an issue at the earliest possible stage. Moreover, regular control helps check any unwanted changes within the system that could cause issues in the future.

10. Avoid nesting layers

Active usage of asynchronous callbacks often leads to a state referred to as callback hell, when it’s really hard to understand what’s going on due to multiple promises across many nested layers. Such code is a perfect place to hide malicious injections. Moreover, in cases with the number of layers exceeding 10, it often causes data loss and other unexpectable issues.

That’s why it’s good practice not to consider JS code from top to bottom, string by string. This helps avoid nesting where applicable.

11. Use Strict Mode

Strict Mode is a special JS mode with restricted functionality. This mode is a way to eliminate some legacy threats JavaScript has had for years. It also has the possibility to detect and fix some bugs invisible in the usual mode, as well as to optimize the performance of the app. Activating the mode in Node.js requires writing “use strict” at the top.

12. Don’t block the event loop

Some requests coming through the event loop may block it from smoothly processing further requests. Such blocks seriously affect overall system performance. Sometimes in cases of enormously high CPU load, this can even stop the event loop.

For not blocking the event loop, you should always control your callbacks, ensuring that they are quickly executed. One of the best solutions is to control the length of your callbacks to keep the number of their steps constant no matter what.

13. Avoid uncaught exceptions

The default Node.js behavior when expecting an uncaught exception is to print the current state trace and then terminate the process. Such uncaught exceptions when not handled often lead to different Node.js security vulnerabilities.

You should always control your exceptions and customize the system’s behavior using an EventEmitter object.

14. Send only what is necessary

You should strictly control the data you’re sending anywhere outside your system. First of all, this data can contain something belonging to your customers or any other third party. Second, your data is the number one source for attackers looking for something that will help them invade. Double-check all the forms, emails, and APIs in order to only send what is absolutely necessary.

15. Use access control actively

For every single area inside your app, it's critical to understand the user permissions. An access control feature is the best option here. Most of the Node.js tools, however, basically have no access control functionality.

Using access control on each request helps identify all the possible routes of a data breach, and, in the case of any issue arising, locates and fixes it much quicker. An elegant solution is to use middleware for implementing the access rules.

Top tools to enhance Node.js security

Node.js API inspector

The Node.js API inspector tool is a highly useful debugging solution that uses the Blink rendering engine’s developer tools. Supporting features:

  • source files routing;
  • CPU profiling;
  • breakpoints;
  • requests inspection;
  • console output inspection, etc.

For installing the inspector, use the next code:

XSS-Filters

This NPM package acts to prevent XSS scripting attacks by validating the output. Using this tool, you can apply context-sensitive output filters, as well as auto-check the output for HTML5 specification compliance.

Bunyan or Winston logging options

Bunyan and Winston are logging libraries. Both provide flexible logging options. For Winston, there is an impressive list of what they call transports — storage devices for your logs. Bunyan, in turn, uses a JSON format and builds logs in the form of JSON.stringify strings.

Toobusy-js module

The core feature of toobusy-js is to remove native dependencies, keeping the server “not too busy”. This is the alternative to using the unref method. As a result, in the case of overload, the server continues to process as many requests as possible rather than crashing. Here you can also see a couple of live examples of the toobusy-js usage.

Event emitter

When we speak about the browser side, we’re referencing events such as mouse clicks, hovers, finger taps for touchscreens, and any other elements of interaction. The event.js module refers to likewise events within the backend entities, controlling them in full. This module allows the creation of a special EventEmitter class that, as we said before, makes it available to efficiently control the exceptions.

Authentication solutions

OAuth, Firebase Auth, or Okta are secure solutions for convenient authentication. All of them offer advanced captcha tools to prevent automated logging in. These are quite powerful against brute force and some types of DDoS attacks, making robots unable to pass the human-oriented step of authentication.

NGINX

NGINX is the most popular web server across both independent developers and companies offering web development services. In our case, this is a way to execute your web app based on Node.js in a more secure way. This approach helps to cache static content, increasing the load speed as well as balancing the load better. This secure Node.js server is often used as a reverse proxy for secure Node.js applications.

Cloudflare

Cloudflare offers a string of products that increase security in different ways. The simplest and most famous is their DDoS protection screen. However, you can find many other tools to minimize your app’s Node.js security issues.

Firewall

Tools such as node-firewall help filter out server requests based on roles and permissions. This is just another way to control access, keeping the security of your Node.js server high. You can learn more about this package here.

Validatorjs

The validatorjs library is a convenient way to organize the validation process within both Node.js and the browser. It offers human-friendly declarative validation rules and simple usability.

Conclusion

The Node.js technology itself is quite secure and has no significant known vulnerabilities, which is one of the Node.js advantages. All the discovered flaws are quickly fixed in further updates thanks to the huge popularity of Node.js. That being said, there are various third-party packages within the Node.js ecosystem that already have (or will have) flawed security protocols or could potentially be used for invasive attacks.

To wrap up:

  • Keeping your development tools up to date is the number one rule in ensuring the security of your software.
  • The second rule is to avoid any questionable or doubtful packages.
Related posts
Get the latest updates on the platforms you love