Survey of best practices for Securing and Scaling Node.js
It is very important to scale and secure your applications. Scaling and securing are not one-time tasks. You need to keep making changes to your code as you add new features to increase application security, and as your application traffic and data increases, you need to scale your servers. In this article, you will learn how to make Node.js applications more secure and how to scale Node.js applications.
I will be assuming that you are using Express for creating your web server as it is the most common.
In this article, we will cover:
- Application vulnerabilities
- Non-vulnerability attacks
- Various third-party services to protect your application
- Checking security issues in third-party packages
- Techniques of distributing traffic
Common vulnerabilities in applications
According to Wikipedia, a vulnerability is a weakness in the application that allows an attacker to reduce a system's information assurance. Applications expose different types of vulnerability.
Let's look at some important vulnerabilities and how to prevent them.
Helmet is a Node.js library that helps you prevent various attacks by setting various security-related HTTP headers.
Here are the various headers added by Helmet:
- Strict-Transport-Policy: This header is used to enforce secure (HTTP over SSL/TLS) connections to the server. HTTPS prevents man-in-the- middle attacks. In a man-in-the-middle attack, an attacker secretly alters the communication between the client and server. This is done to steal data, add ads to web pages, and so on.
- X-Frame-Options: This header provides clickjacking protection. Clickjacking is a technique by which an attacker uses multiple transparent or opaque layers to trick a user into clicking on a button or link on another page when they intended to click on the top-level page. Thus, the attacker is "hijacking" clicks meant for their page and routing them to some other page, most likely owned by another application, domain, or both. This header prevents the application from being viewed inside an iFrame, therefore providing clickjacking protection.
- X-Content-Type-Options: Browsers can override response Content-Type headers to guess and process data using an implicit content type. While this can be convenient in some scenarios, it can also lead to some kinds of attack, such as a MIME confusion attack, authorized hotlinking, and so
on. Returning X-Content-Type-Options will cause browsers to use the provided Content-Type header and not interpret the content as a different content type.
To learn more about Helmet, visit https://www.npmjs.com/package/helmet
Cross-site request forgery
Cross-site request forgery (CSRF) is a type of attack in which requests from a user are sent to servers without the user knowing about it.
For example, if an attacker is able to find a reproducible link that executes a specific action on the target page while the victim is logged in to it, he is able to embed such a link on a page he controls and trick the victim into opening it. The attack-carrying link may be placed in a location that the victim is likely to visit while logged in to the target site sent in a HTML e-mail body or attachment.
There are various ways to prevent CSRF attacks. Most CSRF prevention techniques work by embedding additional authentication data into requests, which allows the web application to detect requests from unauthorized locations.
There is a library named csrf (https://www.npmjs.com/package/csrf) for Node. js that lets you prevent CSRF attacks. It provides you middleware to protect Express web servers from CSRF attacks.
We saw earlier what the XSS vulnerability is. We basically saw what a reflected XSS
attack is. There is another type of XSS attack called stored XSS.
Stored XSS occurs when the application stores user input that is not correctly filtered. For example, while chatting, if a message is not sanitized, then both users can run scripts on each other's browsers by sending JS code within <script> tags as messages.
To prevent both types of XSS attack, we should always filter/sanitize user input.
Session fixation is an attack that permits an attacker to hijack a valid user session.
Here are several techniques to prevent session fixation:
- Set session timeouts
- Regenerate session tokens frequently
- When logged out, expire the session token
- Store the user agent and IP address of the user when creating a session and check whether the value matches during the following HTTP requests.
Non-vulnerability based attacks
There are various kinds of attack that can be made on any kind of application, as they depend on loopholes in the application. Still, applications can do a lot to prevent these attacks.
Let's see a few of the most common non-vulnerability-based attacks and how to prevent them.
A denial-of-service (DoS) attack is an attempt to make a server machine unavailable to its intended users temporarily. An attacker uses one or many machines to make continuous requests to the server to take it down.
The best way to prevent DoS is to use an external service such as CloudFlare, which uses a lot of different techniques and data from various sources to block malicious requests on your server. It's always better to avoid handling DoS on your server and leave it to a service created by DoS experts.
Brute force attacks
A brute force attack aims at being the simplest kind of method to gain access to a site: trying usernames and passwords, over and over, until it gets in.
Here are several ways to prevent brute force attacks:
- We can embed CAPTCHA in forms that can completely prevent bots from making brute force attacks and slow down brute force attacks made by humans.
- There is a middleware program for Express servers called express-brute that limits the rate of incoming requests based on several factors. You can find out more about express-brute at https://www.npmjs.com/package/ express-brute.
Using secure packages
The npm packages you use may contain critical security vulnerabilities that could also affect your application. It's not possible to go through every package's code or test each of them separately.
There is a database called Node Security Project that has a list of the most important vulnerable packages. You can use command-line tools such as nsp (https://www.npmjs.com/package/nsp) and requireSafe (https://www.npmjs.com/ package/requiresafe) to check the vulnerable dependencies of your application.
You should always keep an eye on the new version releases of the packages that your application is dependent on and update it, as a new release often fixes issues related to security.
Scaling Node.js servers
If your application has lots of users accessing the system simultaneously, then obviously a single server cannot handle all the traffic. It will slow down and crash. Therefore, we need to deploy the application on multiple servers and then distribute the traffic equally between them.
To distribute traffic between servers, we need to use something called a load balancer. A load balancer is a server that sits in front of the application servers. The client communicates with the load balancer instead of the application servers, and instead of handling the request, the load balancer forwards it to an application server; when the application servers sends the response, it sends the same response to the client.
As a load balancer doesn't actually process the request, it can handle many more requests than an application server. Obviously, a load balancer cannot handle unlimited requests, so we can use multiple load balancers. When we use multiple load balancers, the traffic between them is distributed by using the round-robin DNS technique. In round-robin DNS, the IP address of the domain pointing to the load balancer changes according to an appropriate statistical model.
Amazon Web Services (AWS) provides a load balancer called Amazon ELB, which can be used to distribute traffic between Amazon EC2 servers, that is, application servers. Obviously, it difficult to predict the total number of EC2 instances you would need to scale your application; therefore, AWS also provides something called auto scaling, which can add/remove EC2 instances as needed. Therefore, to host a large-scale application, Amazon is the best choice. It also provides lots of other cloud services to scale and deploy your application.
In case you don't want to worry about scaling, deploying, and managing your servers, then you can use cloud services such as Heroku, which makes it much easier to achieve all this, and you just need to worry about the application code—that's it.
In this article, we saw a lot of services and libraries to scale and secure Node. js applications. We saw various vulnerabilities and how to prevent them. Make sure you take regular backups of your data so that even if your App is hacked,
you will still have a chance to get the application running again as the data is not lost. Obviously, there is a lot more to learn about scaling and securing Node.js applications as this is an unending topic and new things come up regularly.
Here are related articles if you wish to learn more advance topics for web development:
Comprehensive overview of Angular 2 architecture and features
How Bootstrap 4 extensible content containers or Cards work
Comprehensive guide for migration from monolithic to microservices architecture
Comprehensive overview of Bootstrap 4 features for user interface customizations
Intro to functional reactive programming for advance js web development
Using advance js and webrtc for cross browser communications in real time
Intro to real-time bidirectional communications between browsers and webSocket servers
Junior or senior web developers can also explore career opportunities around blockchain development by reading below articles:
Blockchain Developer Guide- Comprehensive Blockchain Ethereum Developer Guide from Beginner to Advance Level
Blockchain Developer Guide- Comprehensive Blockchain Hyperledger Developer Guide from Beginner to Advance Level