Securing An Express Application
Tags: security, node, javascript
Published: 2021-03-12 16:54:22

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/stpcf20kctwqks1ix4z8.png)

### Overview
The focus of the article is to understand how to secure a Node.js and Express application.

### Introduction
Security is a big concern in the digital world. If your application is not properly secure, then its not a matter **if** you get hacked, but **when** you get hacked. Here are a few things you can do to protect your application out in the wild.  

### Security Best Practices
First, lets cover some best practices from [Express](http://expressjs.com/en/advanced/best-practice-security.html#use-helmet). Express tells us that we should disable the **X-Powered-By** header as it provides attackers with information about how are site works. A simple fix would be to install the package [helmet](https://helmetjs.github.io/). Helmet adds some **out-of-the-box** security changes to the application so that its less vulnerable to attacks.
```javascript
const express = require("express");
const helmet = require("helmet");

const app = express();

app.use(helmet());
```
Another recommendation is to **avoid** using the default *Set-Cookie*, but instead use *cookie-session*. The reason for this is the *Set-Cookie* stores the whole session object whereas the *cookieSession* will store only session I.D. For example we can set a cookie with *cookieSession* the in Node.js as such:
```javascript
const express = require('express')
const cookieSession = require('cookie-session')

const app = express()
const expiryDate = new Date(Date.now() + 60 * 60 * 1000) // 1 hour
app.use(cookieSession({
  name: 'trusted cookie', // Don't use Set-Cookie
  path: '/',
  expires: expiryDate,
  keys: ['some random key'] 
}))
 ...
```
To add an extra layer of security on the cookie we can change it's **sameSite** property. By default sameSite is set to **lax** if we change to **strict** the usage of the cookie is restricted to the domain that issued the cookie.
```javascript
const express = require('express')
const cookieSession = require('cookie-session')

const app = express()
const expiryDate = new Date(Date.now() + 60 * 60 * 1000) // 1 hour
app.use(cookieSession({
  name: 'trusted cookie', // Don't use Set-Cookie
  path: '/',
  expires: expiryDate,
  keys: ['some random key'],
  sameSite: 'strict'
}))
 ...
``` 
Next, we want to make sure our dependencies do not have security issues. We can run a **npm audit**, or use [snyk](https://snyk.io/) to check for security issues in our dependencies. For example, testing dependencies with snyk will produce the following output:
```
Testing /Users/meddy/projects/demo...

Organization:      creativethoughtz.team
Package manager:   npm
Target file:       package-lock.json
Project name:      demo
Open source:       no
Project path:      /Users/meddy/projects/demo
Licenses:          enabled

✓ Tested 56 dependencies for known issues, no vulnerable paths found.
```
Synk is one option, but we also can use just regular npm. With npm we can run a **npm audit fix** to scan our project for vulnerabilities and automatically install any compatible updates to vulnerable dependencies. To see the complete list of recommendations from the Express Team visit [Security Best Practices](https://expressjs.com/en/advanced/best-practice-security.html). At this point the application has minimal defenses. Lets see how we can improve the application security. 

### Cross-Site Request Forgery (CSRF)
[Cross-Site Request Forgery](https://owasp.org/www-community/attacks/csrf) is one of the most common attacks used on web applications. The attack happens when a web server provides a user with an access key of some type, perhaps a cookie or token, so the user may avoid re-authentication. Once the user visits another website where a CSRF attack is set up, the malicious website will be able to make request to the server on behalf of the user. To prevent CSRF attacks use the [csurf](https://www.npmjs.com/package/csurf) package. The csurf package ensures that all request made to the server come from your website. The csurf package enables you to store cryptographic tokens within the forms of your website. When a request is made to the server the payload must contain the token stored within the form. 
#### Example server
```javascript
const express = require('express')
const cookieSession = require('cookie-session');
const csrf = require('csurf')
const expressHandlebars = require('express-handlebars');
const bodyParser = require('body-parser')
 
// setup csrf Protection middleware
const csrfProtection = csrf();

const parseForm = bodyParser.urlencoded({ extended: false })
 
const app = express()

app.engine('handlebars', expressHandlebars({ defaultLayout: 'main' }));
app.set('view engine', 'handlebars')
const expiryDate = new Date(Date.now() + 60 * 60 * 1000) // 1 hour
app.use(cookieSession({
  name: 'session',
  path: '/',
  expires: expiryDate,
  sameSite: 'strict',
  keys: ['some random key']
}))
 
app.get('/form', csrfProtection, function (req, res) {
  // pass the csrfToken to the view
  res.render('send', { csrfToken: req.csrfToken() })
})
 // when a post is made verify the token
app.post('/process', parseForm, csrfProtection, function (req, res) {
  res.send('data is being processed')
})
```
#### Basic form with _csrf token
```html
<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>
```

Another approach to provide CSRF protection is to check the **origin** and **referrer** [headers](https://developer.mozilla.org/en-US/docs/Glossary/Forbidden_header_name) which are known as forbidden headers. Forbidden headers are headers that cannot be modified programmatically because the user agent retains full control over them. These headers contain the host from which the request was made, and we can use this information to compare that to the host of our application.
![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/pctt9rdgfdk9zm2k0kgy.png)
This will help provide an extra layer of security against CSRF attacks. Lets continue exploring other security options for our application.

### Cross-Site Scripting (XSS)
[Cross-Site Scripting](https://owasp.org/www-community/attacks/xss/) is when an attacker is able to inject malicious code within your application. The good news is that if you're using a frontend framework such as Angular, React, or Pug then your data will be sanitized by the framework protecting you from XSS. However, the only way to ensure the data within the database is sanitized is by sanitizing the data on the server. We can use the package [sanitize-html](https://www.npmjs.com/package/sanitize-html) to sanitize data.
```javascript
const sanitizeHtml = require('sanitize-html');
const dirty = 'some really tacky <script>alert("Hi")</script>';
const clean = sanitizeHtml(dirty);
```
You can take this approach if you are not using a framework to render your frontend or if you do want any html tags stored within database. To learn more about the different types of XSS attacks and how to prevent them check out the [OWASP Cheat Sheet](https://cheatsheetseries.owasp.org/cheatsheets/Cross_Site_Scripting_Prevention_Cheat_Sheet.html).

### Rate Limiting
[Rate Limiting](https://cloud.google.com/solutions/rate-limiting-strategies-techniques) is another defensive mechanism we can deploy to protect our resources from exploitation. Rate Limiting will limit the number of request that can be made to the sever. When a maximum number of request have been reached the server will limit request from that source. 

### Authorization
Authorization represent the privileges of a user on our system. The privileges are in reference to a particular resource, and are defined by the acronym *CRUD*, which stands for create, read, update, and delete. When determining the privileges of a user the rule of thumb is *Principle of least  privilege*. This means you should grant privileges only as needed to the users of the system. 

### Authentication
[Password Authentication Protocol (PAP)](https://en.wikipedia.org/wiki/Password_Authentication_Protocol) is one of the weakest authentication schemes, yet the most used. Passwords are easily hacked, and even worse their daisy-chained. The problem arises from the fact the average users has over 90 online accounts. Therefore, if our application requires a password for authentication, then the application should enforce strong password requirements. This will help ensure that our authentication scheme is not the weakest in the chain. Also, we should consider the encryption algorithm for passwords to prevent password cracking. When choosing a hashing algorithm we should avoid encryption methods using the [sha2](https://en.wikipedia.org/wiki/SHA-2) hashing algorithm and instead use methods that use the [argon2](https://en.wikipedia.org/wiki/Argon2) hashing algorithm as it is more secure.
#### hashing code snippet
```javascript
const argon2 = require('argon2');

try {
  const hash = await argon2.hash("password");
} catch (err) {
  //...
}
```
#### verify password code snippet
```javascript
try {
  if (await argon2.verify("<big long hash>", "password")) {
    // password match
  } else {
    // password did not match
  }
} catch (err) {
  // internal failure
}
```
If it is possible we should avoid building our own authentication system, and look to leverage an existing authentication system. One popular authentication system is [passport](https://www.npmjs.com/package/passport). Passport provides us with options for authentication. We can delegate the entire authentication process to use **OAuth**, or **SAML** standards, or if we want to manage authentication ourselves we can use a **Local** strategy.
```javascript
passport.use(new LocalStrategy(
  function(username, password, done) {
    User.findOne({ username: username }, function (err, user) {
      if (err) { return done(err); }
      if (!user) { return done(null, false); }
      if (!user.verifyPassword(password)) { return done(null, false); }
      return done(null, user);
    });
  }
));
```
Another concept gaining popularity is [passwordless](https://auth0.com/docs/connections/passwordless) authentication. Passwordless authentication allow users to log in without the need to remember a password. Instead, users enter their mobile number or email address and receive a one-time code or link, which they can then use to log in. We now have a few options for authenticating users on the web, lets continue increasing the security of the application.

### HTTPS (Data In Transit)
[HTTPS](https://developer.mozilla.org/en-US/docs/Glossary/https) is probably one of the simplest security mechanism you can employ to protect the integrity of your data. Https encrypts the data while it is in transit, making it extremely difficult for hackers to gain access to the information being exchange between client and server.

### AES-256 (Data At Rest)
Another security feature we can use to protect our application and resources is to encrypt the data while its stored in the database or at rest. A strong encryption algorithm such as [AES-256](https://en.wikipedia.org/wiki/Advanced_Encryption_Standard) can be used to encrypt data at rest. One popular approach, that uses AES-256, to encrypt data at rest is [AWS KMS Envelope Encryption Strategy](https://docs.aws.amazon.com/kms/latest/developerguide/concepts.html#enveloping). The scheme uses a master key to encrypt a data key, which the data key can then be used to encrypt the data at rest. When we want to decrypt our data we must use the same data key that was used to encrypt the data at rest.

### Have A Plan
Having a security plan will be the ultimate determinant of your security initiatives and survival of your application. Knowing what to do, who to notify, the type of attack, and how to respond is something outlined in a security plan. A security plan is usually something produce by a security team, which is outside the scope of this article. However, the [AWS Security Whitepapers](https://docs.aws.amazon.com/wellarchitected/latest/security-pillar/wellarchitected-security-pillar.pdf#welcome) outlines some of the best security practices in the industry, many which they use in their own software projects.

### Conclusion
As always, take care, and thank you for reading this article. If you found this article helpful please leave a rating or comment, or if you have question don't hesitate to ask.