« Previous Tutorial Next Tutorial »

Now that our app is production-ready, we want to spend a bit of time making sure it’s as secure as possible. To that end, we’re going to run through some of Express’s best practices for securing your server, and fix a few issues that come up as a result.

Let’s run through some of their suggestions. First up is “don’t use deprecated or vulnerable versions of Express.” This … seems like good advice. Fortunately, we’re using the latest and greatest version of Express, so we should be all set there.

Next up is “Use TLS” – which means running a secure server over HTTPS. This used to be a real pain, and cost money. You can now do it easily and for free, but it requires having a server, which we haven’t gotten to yet. We’ll cover setting up TLS in a couple of tutorials.

“Use Helmet” is their third suggestion, and it’s a good one. Helmet is a collection of middleware functions that adjust your HTTP headers to improve security. It’s super easy to use, so let’s get it installed. First jump into a terminal window or command prompt, cd to your musiclist directory, and type:

yarn add helmet

While that runs, switch to Sublime, open /app.js, and under line 12 add this code:

const helmet = require('helmet');

Then find line 50 and, below it, add this:

app.use(helmet());

That’s all we have to do, so save the file. Next up on Express’s list is “Use cookies securely”. This is, obviously, another good suggestion. To use secure cookies with our session manager, Express-Session, we’re going to need to have TLS enabled on our server. We can make some changes now to app.js to prepare for this, though (and to allow us to continue to work with insecure cookies on our dev server).

First, we’re going to pull our session config out of the require call, so find line 7 and then cut lines 8 to 11, leaving only the semicolon. Also remove the opening paren and brace at the end of line 7. This will give you a line 7 that looks like:

const expressSession = require('express-session');

Now we’re going to work on express session, which is in the big chunk of app.use calls starting on line 41. Normally I'd move this into its own section, but it needs to happen before the passport initialization or our app will break, so just take line 49 and expand it to all of this stuff:

// Express Session
const sessionValues = {
  cookie: {},
  name: 'sessionId',
  resave: false,
  saveUninitialized: true,
  secret: appConfig.expressSession.secret,
};
if (app.get('env') === 'production') {
  app.set('trust proxy', 1);
  sessionValues.cookie.secure = true;
}
app.use(expressSession(sessionValues));

That should be it, so save the file and let’s move on to the next suggestion, “Ensure your dependencies are secure.” This is a big one … we’re going to use a third party tool called snyk, which will allow us to evaluate our project’s dependencies and list vulnerabilities.

So, switch to a terminal window or command prompt and type the following:

npm install -g snyk

Once that’s run, make sure you’re in your musiclist folder and type

snyk auth

This will open a web browser that’ll allow you to authenticate via Github, Bitbucket, or Google, which is a necessary step to get snyk working for you. Click the authorize button and you should be all set. Head back to your terminal window and type:

snyk test

It’ll run for a while, and then output a whole ton of scary errors, most of them low severity, a few of them high. We’re going to take care of them using Snyk’s awesome wizard capability. So, as it suggests at the end of its report, type the following:

snyk wizard

It’ll ask if you want to use Yarn or NPM when applying updates. Since we’ve been using Yarn all along, let’s stick with that. For each vulnerability you will get various options including re-installing, patching, or ignoring. I recommend reinstalling in basically all cases where it’s available. In a couple of instances you’ll be given the option to upgrade instead. That’s also fine.

At the end it’ll also ask you if you want to add a “snyk test” script to package.json. It defaults to “no” and for now, let’s not worry about it. So hit enter or N to continue. It’ll run some stuff in the background for a while, and then finish up (weirdly it tells me “package.json is not a node project” at the end, but it doesn’t seem to matter).

You’re going to be left with a couple of vulnerabilities no matter what, because not every module has been fully patched, but there should only be a few. Snyk is good about alerting you to new vulnerabilities and new patches, so keep an eye out! You can re-run snyk wizard any time.

There are two other things we really should cover in this tutorial. The first is rate-limiting. Rate limiting ensures that someone doesn’t come along and just hammer your API with requests, which can be an effective denial-of-service attack. Let’s make it so that users can only hit our API fifty times per minute. That’s actually a really high rate, but we’re not expecting to have a ton of users, and you could always adjust it based on traffic further down the line.

Still in the terminal window, type the following:

yarn add express-rate-limit

Let that run, then switch to sublime, open app.js, and below line 14 add this code:

const RateLimit = require('express-rate-limit');

Then find line 80, add a padding line below it, and add this code:

// configure rate limiter
const apiLimiter = new RateLimit({
  windowMs: 1  60  1000, // 1 minute
  max: 50,
  delayMs: 0, // disabled
});
app.use('/api/', apiLimiter);

This will attach the rate-limiter middleware to any requests sent to our API. Nice. Save this file.

The second thing we need to cover is input sanitization. The last thing you want is someone injecting malicious JavaScript into your site or gaining control of your database via injected commands. Unfortunately there’s no easy single-step solution to sanitizing incoming data. You have to make changes to every API endpoint that receives text from users.

In the spirit of saving some time, I’m going to show you an example of what to do, but leave it up to you to apply the changes across your various endpoints.

First, let’s install a simple string sanitizer. In your terminal window or command prompt, type:

yarn add dompurify jsdom

When it’s done, head for Sublime and open /routes/api/authentication.js. Below line 2, add this code:

const createDOMPurify = require('dompurify');

Then add the following below line 4:

const { JSDOM } = require('jsdom');

Now head for your register routine on line 53. Below line 61, add the following code:

    // sanitize data
    const window = (new JSDOM('')).window;
    const DOMPurify = createDOMPurify(window);
    const sanitizedBody = {
      username: DOMPurify.sanitize(req.body.username),
      email: DOMPurify.sanitize(req.body.email),
      firstName: DOMPurify.sanitize(req.body.firstName),
      lastName: DOMPurify.sanitize(req.body.lastName),
      password: req.body.password,
    };

Now change line 73 to this code:

    const newUser = new User(sanitizedBody);

And line 79 to this:

        return res.send(JSON.stringify({ error: err.message }));

We’re good to go, here. Save the file. Now if a user tries to inject script or other markup into those values, it’ll get eliminated. This does mean that if they happen to put the script tags in their username or email address, they’ll get a slightly confusing “no username” error, but you know what? I’m not real worried about catering to people who’re trying to break my site!

That’s basic sanitation. It’s advisable that you do this for ALL of your API routes, especially if your API is public-facing.

So, that’s about it for securing your app. This is the kind of stuff that becomes increasingly important as your application gains traction and users, to the point that some developers make a very good living just specializing in securing people’s APIs. This tutorial is meant as a starting point only. If you’re trying to deploy a production application, please do a whole lot more research into this area before going live!

In our next tutorial, we’re going to sign up with a host, create a server, and start setting it up. See you then.

« Previous Tutorial Next Tutorial »