How to Secure your NodeJs Express Javascript Application - part 2
Tags: javascript, node, express, security
Published: 2022-02-21 10:51:03

Express is an easy-to-use web framework for NodeJs. In this article, we will review some of the common vulnerabilities in NodeJs Express projects and explain mitigations against them.

Let's see how we can make our Express software more secure.

## Limit input
Allowing clients to send huge request body can be abused for a denial of service (DoS) attack. To prevent DoS attacks always set a maximum request size for the input. Use the below code to limit input size.

```JavaScript
var contentType = require('content-type')
var express = require('express')
var getRawBody = require('raw-body')

var app = express()

app.use(function (req, res, next) {
  getRawBody(req, {
    length: req.headers['content-length'],
    limit: '1mb',
    encoding: contentType.parse(req).parameters.charset
  }, function (err, string) {
    if (err) return next(err)
    req.text = string
    next()
  })
})
```

You can also set request size for different content types:

```JavaScript
app.use(express.urlencoded({ extended: true, limit: "1kb" }));
app.use(express.json({ limit: "1kb" }));
```

Other than input size you should limit the Transaction per Second/Minute (TPS/TPM) rate. If you don't set a rate limitation for your sensitive APIs like login, you expose yourself to a [Brute Force](https://www.thesmartscanner.com/vulnerability-list/brute-force-prevention-bypassed) attack. Packages like [express-brute](https://www.npmjs.com/package/express-brute), [express-bouncer](https://www.npmjs.com/package/express-bouncer) and [express-limiter](https://www.npmjs.com/package/express-limiter) are some sample packages you can use for limiting input rate in Express. Below code is a sample code for protecting against brute force attacks.

```JavaScript
const express = require('express')
const ExpressBrute = require('express-brute');
const login = require('./routes/login.js')

const app = express()
 
// using memory store, an in-memory db is recommended for production
let store = new ExpressBrute.MemoryStore();
let bruteforce = new ExpressBrute(store);
 
app.post('/auth', bruteforce.prevent, login);
```

**Note**: If you use an API gateway like Kong you can easily configure input limiting policies for your routes.

**Note**: CAPTCHA and account lockout are also other common solutions for mitigating brute-forcing attacks.

**Bonus**: You can also benefit from packages like [toobusy-js](https://www.npmjs.com/package/toobusy-js) for monitoring the load of your server to detect whether you're under a DoS attack.

## Remove old, forgotten, debug, and unused routes
It's common (but not recommended) to add debug routes during development for testing purposes. These routes usually don't have authentication and they're less restricted. The path to these routes is usually easy to guess; something like `/test`. This allows attackers to find them easily and abuse the functionality.

Review all the routes in your Express project and remove any unnecessary routes and use proper testing and versioning scheme to prevent unwanted routes in production.

**Bonus**: Another example of unwanted routes are default routes like those available in [Sails](https://sailsjs.com/) and [Feathers](https://feathersjs.com/). Fortunately Express only has a default error and 404 handlers which we will cover in the following section.

## Catch 404 and 500 errors
You should always expect exceptions to happen, so get prepared. Unhandled exceptions throw a ton of information to users. Default NodeJs exceptions disclose information like Operation System and used libraries.


![Express JavaScript Detailed Error](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/0rjvp6wi0010mikwyqxf.png)

Instead of displaying a [detailed error message](https://www.thesmartscanner.com/vulnerability-list/detailed-application-error), you can add an error-handling middleware to catch all errors like below.

```JavaScript
app.use((err, req, res, next) => {
 console.error(err.stack)
 res.status(500).send('Something broke!')
})
```

**Note**: You should add the above middleware after any other `app.use()` and routes.
No need to mention that you can capture any exception in NodeJs by subscribing to `uncaughtException` event on the `process` object.

## Write clean code
Code quality is directly correlated to software security. Less code hides fewer vulnerabilities, right? So does the clean code. Keeping your Express project as clean as possible helps to increase the security of your JavaScript application.

One example of bad code that is common in JavaScript projects is the *Callback Hell*. Callback Hell, also known as *Pyramid of Doom*, is an anti-pattern seen in code of asynchronous programming. It happens when callbacks are nested inside each other. It's easy to get lost in a callback hell because it's hard to follow the execution flow of the program. Inside those deep nested callbacks security bugs, unhandled exceptions, and information leakage are waiting for the right moment to strike.

![JavaScript Promise Hell Refactored](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/scprycnd67p81ewpj1oo.png)

It's important to have a proper Secure SDLC process in place to prevent bad code. Things like enabling security linters, code reviews, and automatic code scan like Github code scan (see this [repo](https://github.com/github/code-scanning-javascript-demo)) are good controls for having more secure code.

## Only return what is necessary
When passing information to the clients always return the minimum possible fields. Consider a use-case where you want to show a list of available users on your website. You can query the database for all users and return a list of users with all their attributes and only display the username on the front end. Properties like username, password, address, social security numbers, and many others are usually associated with users. You might not display all of them on the page but you are exposing them in your API which makes them available for hackers.

## Do not use `eval`
There are some functions you must avoid calling unless you absolutely have to use them. `eval()` is one of them. The `eval()` function evaluates JavaScript code represented as a string. It is far too easy for a bad actor to run arbitrary code when you use `eval()` with user-supplied input. The NodeJs `child_process.exec` is similar to `eval()` and should be avoided.

The `fs` and `vm` modules as well as the `RegExp` class should be used carefully if you want to pass user input to them. Please read the [input validation](https://www.thesmartscanner.com/blog/how-to-secure-your-nodejs-express-javascript-application-part-1#validate-input) on part 1 of this series for more.

## Use `strict mode`
JavaScript's strict mode, introduced in ECMAScript 5, is a way to opt-in to a restricted variant of JavaScript. Strict mode makes several changes to normal JavaScript semantics. It eliminates some JavaScript silent errors by changing them to throw errors. It also helps JavaScript engines perform optimizations and prohibits some syntax likely to be defined in future versions of ECMAScript.
For all these improvements it's encouraged to always use strict mode. For activating strict mode add `'strict mode';` at top of your code.

## Log and monitor events
Record any events on your application and review them. Monitoring logs regularly could help identify malicious activities on your system. When something bad happens, logs are your only way to find out what happened and what is the root cause of it.
In Express you can use [express-winston](https://www.npmjs.com/package/express-winston) for logging.