Securing Nodejs Web Application

Javascript Dec 08, 2020

Statistics show that close to 80% of online web apps aren't secure or are easily hackable therefore security is always a concern. To follow best practices with Nodejs there are several resources to help you determine if your app is insecure. OWASP (open web app security project) is a great resource for threats and cybersecurity information you can explore their website to learn more. Visit nodegoat to see the list of the most common security threats and how to minimize them.

Overview Of Cross-Site Scripting Attack (XSS)

This is when a script is injected into your trusted site. For example, an attacker can add javascript code into a form input and then use this to do all kinds of no good. Some examples of attacks are pulling down data from cookies, session tokens, and all kinds of sensitive information. let me demonstrate an example go to Visit xss demo once you get to the site scroll down until you see a demo section and click on show demo then try a test. This is a typical form which we can use to search so if we input "test" for example there is no threat but if we input a script to alert something then this script will get fired instead of the search.
Alt Text
This is a cross-site scripting attack, and it's very dangerous and s one of the top threats because if a developer doesn't pay attention to this type of issue, it is in most cases the easiest to hack.

How To Prevent This Type Of Attack

  • You need to validate your data both on the server and client so make sure you validate or sanitize the data coming from the input form.
  • You also need to implement output encoding (Output encoding is the process of replacing HTML control characters (e.g., <, >, ", &, etc.) into their encoded representatives).

OverView Of Denial Of Service (DOS)

DOS is basically an attack that renders your needed service such as client, server, or application unavailable to your users. The attacker can achieve this by manipulating the network code or exploit vulnerabilities of your service. In most cases, the attacker overwhelms the server of requests, therefore shutting it down. They often use techniques such as creating too much data on your database, creating a loop mechanism, lock customers account, or failure to release specific resource all through code exploit. A good example is the use of complicated regular expressions to render the server unavailable for a while or hang indefinitely.

How To Prevent DOS Attack

  • Make sure all data from forms are sanitized and validated.
  • Make sure you have a mechanism in place to prevent looping instructions or the creating of multiple objects or data on your database.
  • Avoid writing your own regular expressions make sure you use packages like Valdator.js or a safe-regex package in Nodejs to prevent any regex threats.

Overview Of Server-Side Injection

Server-Side Injection is the activity of injecting untrusted data into a server as part of a command or query. With this injected code, the attacker can trick the server and perform all sorts of no good. The attackers use eval, setTimeout, setInterval and function methods to process malicious code. for example, an attacker could inject a while(1) conditional into an eval function thereby rendering the server useless by using 100% of its resources. Leveraging eval weakness the attacker could also insert commands to read the content of a response call of a known server and pull user data.

How To Prevent Server-Side Injection

  • Always validate and sanitise user input.
  • Never use eval, setTimeout, setInterval and functions to parse user input.
  • Use safer JSON.parse() or parseInt() to parse data.
  • Include use strict in your code.

Maintaining Package Dependencies

Let's explore what commands are available to maintain our dependencies thereby making our app more secure this is one of the biggest things you need to constantly work on in other to properly secure our app built with Nodejs. There are reasons why dependencies are updated and sometimes the cause of the update is due to recently found vulnerabilities in the code and so since we rely on these packages we need to make sure that our applications are also up to date and therefore safer for our own use.

  • The first command is npm audit this will go through all the dependencies installed in your node modules and check f there are any issues or vulnerabilities.
  • Next is to check if there are packages that are outdated so do npm outdated and if any package(s) is/are outdated simple update the package using npm update [email protected] or npm install packageName which will pull down the latest package.

Data Handling With Type And Validation

  • When handling data on your server, the best first line of defense is to do validation and type assertion in your schema this way, the data sent is already sanitized to be of a certain type, and with this dangerous script will not reach your server.
  • Further validate with a library and avoid using your own regular expression.
import validator from "validator";
import * as mongoose from "mongoose";

  const UserSchema = new mongoose.Schema({
  name: {
    type: String,
    required: [true, "name is required"],
  },
  email: {
    type: String,
    required: [true, "email is required"],
    validate: [validator.isEmail, "invalid Email"],
    lowercase: true,
    unique: true,
  },
  password: {
    type: String,
    required: [true, "password is required"],
    minlength: 6,
    select: false,
  },
  confirmPassword: {
    type: String,
    required: [true, "please confirm your password"],
    validate: {
      validator: function (val: string) {
        return val === this.password;
      },
      message: "password do not match",
    },
  },

Use Prepared Statements For SQL/NoSQL

A prepared statement is basically the use of a template in your code with empty values when making queries to the server and then as the command is executed the empty values are replaced with the data needed to fulfill the query. The beauty of most of today's tools for querying database is that they already include them so if you are using mongoose for MongoDB or
Sequelize to query SQL databases, you already have this covered.

Set Proper HTTP Headers With Helmet

This is a great package that automatically adds 12 security headers to your app with few simple lines so go to Helmet and once you get to this site, you will see that s very easy to set up. You can explore the documentation to learn more. To set helmet within your application you will need to install and import the package at the entry point of your application.

import  helmet from "helmet";
import  express from "express";
const app = express()

app.use(helmet())

With this, your app is protected with 12 new HTTP headers. If you want to use additional headers for example nocache, all you need to do is

app.use(helmet.nocache)

Encrypt User Data And Session Management

Another strong recommendation to properly secure your Nodejs app is to encrypt data across all communication channels so when your client and server are exchanging data use Nodejs package like crypto which allows you to encrypt sensitive data with several methods to create hash data secret word and hash algorithm. By having the proper secret and algorithm on your app you can encrypt and decrypt data so that if anyone intercepts your data, you minimize the possibility that they can decipher your data. You can use the crypto module whenever you have two-way communication between your server and client so that nobody intercepts your data.

Also, make sure you properly manage your sessions. If you leave sensitive data in your browser's local storage it's another potential security issue.

Use Secure HTTPs Protocol

The HTTP protocol is basically the same Hypertext Markup protocol that we've been using to browse the web but is secure. It allows you to transfer the webpage you view in a secure manner. This means that communication between your browser and the server is encrypted. So make sure the domain you are using has an SSL Certificate.

Rate Limiting Against DOS Attack

Just as I have stated, DOS (denial of service) attack will overwhelm your server with requests until your server IO is overloaded and therefore shut down. This can be prevented by limiting the number of requests hitting your server in a specific amount of time using express rate limit To set express rate limit within your application you will need to install and import the package at the entry point of your application.

import rateLimit from "express-rate-limit";
import  express from "express";
const app = express()

const limiter = rateLimit({
        windowMs: 10 * 60 * 1000,
        max: 100,
        message: "Too many request from this IP, please try again 
       after 10mins",
      });
 app.use(limiter);

This ensures that a user can make a maximum of 100 requests within a time frame of 10min. The timing depends on you.

Use CSURF To Prevent Cross-site request forgery

Cross-site request forgery, also known as one-click attack or session riding and abbreviated as CSRF (sometimes pronounced sea-surf) or XSRF, is a type of malicious exploit of a website where unauthorized commands are submitted from a user that the web application trusts. In a CSRF attack, an innocent end-user is tricked by an attacker into submitting a web request that they did not intend. This may cause actions to be performed on the website that can include inadvertent client or server data leakage, change of session state, or manipulation of an end user's account. To prevent this kind of attack, we can add csurf protection to all our API endpoints and you can achieve this using the csurf package

On route level, you have to do something like this

import cookieParser from 'cookie-parser';
import csrf from 'csurf';
import express from 'express';
 
// setup route middlewares 
const csrfProtection = csrf({ cookie: true });
const parseForm = express.urlencoded({ extended: false });
 
// create express app 
const app = express();
 
// we need this because "cookie" is true in csrfProtection 
app.use(cookieParser());
 
app.get('/form', csrfProtection, function(req, res) {
  // pass the csrfToken to the view 
  res.render('send', { csrfToken: req.csrfToken() });
});
 
app.post('/process', parseForm, csrfProtection, function(req, res) {
  res.send('data is being processed');
});

While on the view layer you have to use the CSRF token like this:

<form action="/process" method="POST">
  <input type="hidden" name="_csrf" value="{{csrfToken}}">
  
  Favorite color: <input type="text" name="favoriteColor">
  <button type="submit">Submit</button>
</form>

Use Cooke Attributes

Cooke attributes determine the proper use of cookie session with this type of attributes, you have better control of how your cookie sessions are uses and where. Let's explore a few attributes that can be set and then introduce what package can be used to set them in your app. When you create a cookie you have a few options or attributes you can add, for example, you can set the following:

  • secure - this only send a cookie if your request is over HTTPS
  • HTTPOnly - prevent the cookie from being access by javascript
  • Domain - determine the scope of the cookie or you can even be more specific with the actual path to access it.
  • expiry - when the cookie expires.
    You can use cookies or cookie-session to set your cookie.

I hope the above list of security protection helps you to secure your NodeJS application.

Calvin puram

I thrive to design and develop ideas into a project that makes life easy, solve problems, and implement innovations.