Express is a fast, unopinionated, and minimalist web framework for Node.js. It has been a de facto choice for creating a web-server application in Node.js. Read these series if you want to use Express in your application.
Let's see how you can make Express more secure.
Use Updated Express Version (and any other npm package)
Old versions of Express have vulnerabilities like Path traversal (CVE-2017-14849). The best practice is to use the latest stable packages to mitigate such vulnerabilities. You can use the npm audit
command to find out known vulnerabilities in your Nodejs application. Then you can fix them by running the npm audit fix
command. Make sure to get 0 vulnerabilities in the report of the npm audit command.
Secure your HTTP Headers
Proper HTTP headers can prevent security vulnerabilities like Cross-Site Scripting, Click-jacking, Packet sniffing and, information disclosure. It's better not to use Express with its default HTTP headers.
Try the Helmet npm package for hardening the HTTP headers of your Express project. Below is a sample code.
const express = require("express");
const helmet = require("helmet");
const app = express();
app.use(helmet());
// ...
Read the Complete guide to HTTP Headers for more detailed information about HTTP Headers security.
Validate Input
Hackers should find a way into your application and, request parameters are their first choice. All the injection vulnerabilities like SQL Injection, Command Injection, Expression Language injection, and many others occur when unvalidated user input is directly used in performing tasks.
Consider below code which gets your name in the name
query parameter and displays it.
const express = require('express')
const app = express()
app.get('/', function (request, response) {
response.send('Hello ' + request.query.name)
})
app.listen(3000)
If you send a request like http://localhost:3000/?name[foo]=bar
then you will receive an Object instead of a String name. This is an attack known as HTTP Parameter Pollution (HPP). It can be very scary when working with a no-SQL database like MongoDB.
Before processing any request
parameter, validate the following:
- Input type (either String, Number, Boolean, etc.)
- Input boundaries: Check range for numbers, length, and acceptable characters for strings
- Input format: Check for input patterns like emails, IP addresses, etc.
You can use hpp npm package for preventing HPP attacks explained above.
Input validation is a broad topic. It can be very tricky especially dealing with rich user content. You can read this article for an in-depth review.
Sanitize Output
Below sample code exposes a Cross-Site scripting (XSS) vulnerability.
const express = require('express')
const app = express()
app.get('/', function (request, response) {
response.send('Hello ' + request.query.name)
})
app.listen(3000)
If you run the application and open http://localhost:2000/?name=<script>alert(1)</script>
URL, the alert(1)
JavaScript code will be executed. XSS bug allows an attacker to run any client-side code to steal session tokens, passwords or display wrong information.
To prevent the XSS you have to use proper encoding before rendering input parameters in the response. You can use node-esapi or escape-html
Below code is a fix for the above XSS:
const express = require('express')
var ESAPI = require('node-esapi');
const app = express()
app.get('/', function (request, response) {
encodedName = ESAPI.encoder().encodeForHTML(request.query.name)
response.send('Hello ' + encodedName)
})
app.listen(3000)
Use Anti CSRF
Processing form data and performing actions only by relying on the form data will cause a Cross-Site Request Forgery (CSRF).
If the same request data (either form data or URL query) causes the same action on your application then you have a CSRF issue. It gets serious when the action is sensitive like, creating a new user or deleting data.
Attackers use CSRF to perform actions on behalf of an authorized user while the user is unaware of this action. Below sample code is an example that kills the app by a CSRF.
const express = require('express')
const app = express()
app.get('/', function (request, response) {
response.send('<h1>Admin Panel</h1><a href=/kill>kill</a>')
})
app.get('/kill', function (request, response) {
process.exit()
})
app.listen(2000)
You might wonder that adding cookie-based session management cannot prevent CSRF because Cookies are automatically sent by browsers. To prevent CSRF you should send random tokens within each request and validate the existence of the CSRF token before processing the request.
You can use the csurf npm package for integrating CSRF prevention in your Express application.
In next article we will see how to prevent brute force attacks, command execution and information disclosure vulnerabilities.
Top comments (2)
In 2010-2014 - yes, but not in 2022.
Thanks for your comment. I'm aware of many other options available nowadays (fastify, Nest, SailsJS, koa, etc.) But I think Express is still quite popular to call it de facto.