DEV Community

Cover image for HelmetJS and SwaggerUI: Avoiding headaches in your NodeJS app
Chidera Anichebe
Chidera Anichebe

Posted on

2 1

HelmetJS and SwaggerUI: Avoiding headaches in your NodeJS app

As a Software Engineer, the importance of embracing Security-driven development cannot be overemphasized. If you realize how important this is, then you must have used one or more security middlewares such as HelmetJS. These tools work great until you introduce another such as Swagger UI to aid with API documentation.

I have encountered some of these issues and thought it'd be helpful putting some tips out there so you don't end up commenting out app.use(helmet()) at best πŸ™‚

Tips

  1. Always make sure to render the Swagger UI template before setting up the HelmetJS middleware to avoid issues like this:
    Bunch of errors

  2. In most online references, this is how you set up a route to serve Swagger UI

    const express = require('express');
    const swaggerUi = require('swagger-ui-express');
    const swaggerDocument = require('./swagger.json');
    
    const app = express();
    
    app.use('/api-docs', swaggerUi.serve, swaggerUi.setup(swaggerDocument));
    

    This would cause issues in your application as there's a difference between using app.use and an Express application routing method such as app.get. The former is used for requiring middleware and would match all routes that stem from the input route (tears if the input is /) irrespective of the HTTP verb. You'll find yourself sending POST requests to specific endpoints and getting back the HTML page of Swagger, not a good experience!

  3. If you are going to be rendering some HTML page for some reason, ensure you use a templating engine such as ejs or pug to avoid getting screamed at like so:

Content-Security-Policy error

This happens because HelmetJS sets a couple of security headers by default, one of these headers is the Content-Security-Policy header which has a default value of default-src 'self';base-uri 'self';font-src 'self' https: data:;form-action 'self';frame-ancestors 'self';img-src 'self' data:;object-src 'none';script-src 'self';script-src-attr 'none';style-src 'self' https: 'unsafe-inline';upgrade-insecure-requests . This header aims to help mitigate Cross-Site-Scripting (XSS) attacks amongst other things and "safe" inline scripts need to have a SHA256 hash or a nonce to enable execution.

To fix this, we will configure a templating engine (ejs in this case), generate a hash using the crypto package, and then set the script-src directive to 'self' 'nonce-{hash}' like so:

...
const crypto = require("crypto");

app.use((req, res, next) => {
  res.locals.cspNonce = crypto.randomBytes(16).toString('hex');
  next();
});

app.use(
  helmet.contentSecurityPolicy({
    directives: {
      scriptSrc: [(req, res) => `'nonce-${res.locals.cspNonce}'`],
    },
  })
);
Enter fullscreen mode Exit fullscreen mode

After setting the nonce, open up your ejs file and add set the nonce value on the script tag like so:

<script nonce="<%= cspNonce %>">
      const formElem = document.querySelector('#formElem');

      formElem.onsubmit = async (e) => {
        e.preventDefault();
        let url = window.location.href;
...
Enter fullscreen mode Exit fullscreen mode

NB: Do ensure the variable names match, also, If you're going to be including external scripts such as Axios via CDN, ensure you add the nonce to the script tag as well

Thanks for reading and I hope it does help you out, would update this as I encounter more issues. See ya at the next one! πŸ’–

Further reading

  1. Swagger UI documentation

  2. HelmetJS documentation

SurveyJS custom survey software

Simplify data collection in your JS app with a fully integrated form management platform. Includes support for custom question types, skip logic, integrated CCS editor, PDF export, real-time analytics & more. Integrates with any backend system, giving you full control over your data and no user limits.

Learn more

Top comments (0)

SurveyJS custom survey software

JavaScript Form Builder UI Component

Generate dynamic JSON-driven forms directly in your JavaScript app (Angular, React, Vue.js, jQuery) with a fully customizable drag-and-drop form builder. Easily integrate with any backend system and retain full ownership over your data, with no user or form submission limits.

Learn more

πŸ‘‹ Kindness is contagious

Please leave a ❀️ or a friendly comment on this post if you found it helpful!

Okay