express-rest-api-boilerplate icon indicating copy to clipboard operation
express-rest-api-boilerplate copied to clipboard

Feat: JWT_SECRET by default and enforcing a strong secret in production

Open aichbauer opened this issue 5 years ago • 0 comments

We should let the user know if they use a unsafe JWT_SECRET when they are not using production. jsonwebtoken uses HS256 as algorithmen, I read an article that it is possible to actually brute force the secret of a HS256 algorithm when using a "bad" secret. Actually one should use a 256 bit secret. which is the equivalent of 32 charcaters, since 1 char is 8 bit.

It would be better to actually throw when using NODE_ENV=production and not using a strong secret. Also it would be good to let the user know if they are using a "good" secret.

I would recommend something similar to:

// api/services/production.service.js
const productionService = () => {
  if (
    process.env.NODE_ENV
    // we have to choose a strong secret
    && process.env.JWT_SECRET
    && process.env.DB_NAME
    && process.env.DB_USER
    && process.env.DB_USER
    && process.env.DB_PASS
  ) {
    if (process.env.JWT_SECRET.length <= 31) {
      throw new Error('Not safe for production: JWT_SECRET should be at least 256 bit, e.g. 32 characters long');
    }

    return true;
  }

  throw new Error('Not safe for production: you forgot to set one or more environment variables');
};

module.exports = productionService;
// api/services/auth.service.js
const jwt = require('jsonwebtoken');

const productionService = require('./production.service');

const safeSecret = process.env.JWT_SECRET ? process.env.JWT_SECRET.length >= 32 : true;

const secret = () => {
  if (
    (
        safeSecret
        && process.env.JWT_SECRET
    )
    || 
    (
      process.env.NODE_ENV === 'production'
      && productionService()
    )
  ) {
    return process.env.JWT_SECRET;
  }

  if (
        !safeSecret
        && process.env.JWT_SECRET
    ) {
    console.error('\n\n\nYou are using a JWT_SECRET that would not be safe for production. Keep in mind that your secret should be at least 256 bit, e.g. 32 characters long.\n\n\n')
    return process.env.JWT_SECRET;
  }

  return 'secret';
};

const mySecret = secret();

const authService = () => {
  const issue = (payload) => jwt.sign(payload, mySecret, { expiresIn: 10800 });
  const verify = (token, cb) => jwt.verify(token, mySecret, {}, cb);

  return {
    issue,
    verify,
  };
};

module.exports = authService;

aichbauer avatar May 15 '19 19:05 aichbauer