Best security practices in Node.Js that are easy to implement and understand

Best security practices in Node.Js that are easy to implement and understand

For a beginner node.js developer security is one of the major concern as unlike its counterpart Django node.js doesn't come with built-in features for security and we have to manually manage our security flaws.
In this blog we are going to look at couple of common attacks and how to prevent them.

1. Compromised Database: There are a lot of real life instances where attacker gets access to the database and there are no system or methods that is not vulnerable to an attack. So as a beginner we can follow few methods to secure our data:

  • We should never save password in our database without encryption so that even if attacker can't at least steal user's password. There is a npm package named brcypt that can help us in encrypting and decrypting our password.
    To encrypt our password:-
const bcrypt = require('bcrypt');
const password = await bcrypt.hash(this.password,12);

It adds a random string to the password so that two same password generates different hash We can save this hashed password in our database. The 12 is the cost parameter of this encryption. Higher the number higher is the cpu intensive encryption it is going to be. Default value is 10.
To compare password with the hashed password we use:-

await bcrypt.compare(candidatePassword,hashedPassword);

It returns true if password is matched else false.

  • Strongly encrypted password reset token. When we issue password reset token to the user to reset his/her password we should have strong encryption and expiration so that even when attacker gets access to the database and our reset token is going to expired by then. It is good practice to make our reset token last only 5-10 minutes. We can use node.js built in module called crypto for this operation.

    const crypto = require('crypto');
    const createPasswordResetToken = function () {
      const resetToken = crypto.randomBytes(32).toString('hex');
      const passwordResetToken = crypto.createHash('sha256')
                     .update(resetToken).digest('hex');
      const passwordResetExpires = Date.now() + 10 * 60 * 1000;
    
      return resetToken;
    };
    

    It is very secure as it is generating 32 bytes hexadecimal string and then creating hash using sha256 algorithm and it expires in 10 minutes.

2. Brute Force Attacks: It is an attack where an attacker tries to guess a password by trying millions of random passwords until they find the right one.
These are few methods we can use to prevent this:

  • By using bcrypt we can make logging process slower.Because if it takes more time to hash the value, it also takes a much longer time to brute-force the password. Keep in mind that slow means that it requires more computing power. The same goes for when a potential hacker tries to brute-force a password.

  • We can also implement rate limiting i.e to limit the number of request coming from one single IP. We can use npm package called express-rate-limit

    const rateLimit = require('express-rate-limit');
    const limiter = rateLimit({
           max: 100,
           windowMs: 60 * 60 * 1000,
           message: 'Too many requests from this IP, Please try again in an hour',
    });
    

    Max property is the maximum number of request that can come from the same IP. windowMs is the window(in Milliseconds) in which max request can be send and the message is the error message that user going to get if the maximum limit is crossed.

NOTE: If you are building an API that needs a lot of requests from the same IP then max number should be higher.
We can then use this limiter with our express middleware to limit certain routes.

const express = require('express');
const app = express();
app.use('/api', limiter);

It is going to limit every route starting with /api.

3. Cross-Site Scripting (XSS) attacks: XSS is an attack where the attacker tries to inject scripts into our page to run his malicious code.
There are few methods we can use to prevent XSS attacks:-

  • On client side xss attacks is especially dangerous as it allows attacker to read local storage, which is the reason why we should never store JWT tokens in the local storage. Instead it should be stored in a HTTP-only cookie. HTTP-only means that the browser can only recieve and send the cookie but cannot access or modify it in any way.
const cookieOptions = {
    expires: new Date(Date.now() +24 * 60*60*1000),
    httpOnly: true,
    secure: true
};

Expires is the time for which cookie is valid. Secure means HTTPS protocol only. If you are using HTTP protocol please remove it.
Then we can send the response back to the client

res.cookie('jwt', token, cookieOptions);
  • NoSql query attack is one of the most important attacks to defend against if you are using noSql database like mongoDB. We can use the npm package called expres-mongo-sanitize
const mongoSanitize = require('express-mongo-sanitize');
app.use(mongoSanitize());

This middleware will look at req.body and req.params and filter out all of the '$' sign and dots because that's how mongoDB operators are written.

  • To clean any input from malicious HTML codes we can use npm package called xss-clean.
const xss = require('xss-clean');
app.use(xss());
  • We can use special security HTTP header using a npm package called helmet
const helmet = require('helmet');
app.use(helmet());

4. Denial-Of-Service (DOS) attacks: DOS attack happens when an attacker sends so many requests to the server that it breaks down and the application becomes unavailable. There are few methods that we can implement to overcome DOS attacks:-

  • Implementing rate limiting is one of very good solutions to the DOS attacks.

  • We should also limit the amount of data that can be sent in a body of post or patch request.

app.use(express.json({ limit: '10kb' }));

Here we set the limit for user input to 10Kb. You can set it to the requirements of your API.

5. Few more best security practices are :

  • Never commit any configuration file like environment variable to a public version control like GitHub because it contains the most sensitive data of your entire application.

  • Whenever there is an error don't send the entire error to the client. So, stuff like stack trace could give attacker some valuable insights into your system.

  • You can also implement two-step authentication in your API.

If you have enjoyed my article and wants more content on Node.js then please follow me on Twitter at twitter.com/prklm10 . Have a good day.