Categories
Node.js Best Practices

Node.js Best Practices — Security Attacks

Node.js is a popular runtime to write apps for. These apps are often production quality apps that are used by many people. To make maintaining them easier, we’ve to set some guidelines for people to follow.

In this article, we’ll look at some basic security practices to be aware of writing Node apps.

Prevent Query Injection Vulnerabilities with ORM/ODM Libraries

We should never pass in user-inputted strings straight into our app to prevent SQL or NoSQL injection attacks. Inputs should be validated and sanitized before being passed into database queries.

All reputable data access libraries like Sequelize, Knex, and Mongoose have built-in protection against script injection attacks.

Unsanitized strings can easily destroy data and expose them to unauthorized parties if they’re left unsanitized.

Collection of Generic Security Best Practices

We should keep up-to-date with general security best practices so we can implement them when we’re developing and running apps.

Adjust the HTTP Response Headers for Enhanced Security

We can use modules like helmet to secure headers to prevent attacks from using common attacks like cross-site scripting with our apps.

To add helmet and use it, we run:

npm i helmet

and then use it as follows:

const express = require('express');
const bodyParser = require('body-parser');
const helmet = require('helmet');
const app = express();
app.use(helmet());

app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: true }));

app.get('/', (req, res) => {
  res.send('hello');
});

app.listen(3000, () => console.log('server started'));

Helmet automatically protects us from cross-site scripting, enables strict transport security, and keep clients from sniffing the MIME types from responses.

The X-Powered-By header is also removed from the response so that attackers won’t know that our app is an Express app.

Constantly and Automatically Inspect for Vulnerable Dependencies

We can use npm audit or snyk to check for packages with vulnerable dependencies before going to production. Otherwise, attacks may take advantage of the vulnerabilities to commit attacks.

Avoid Using the Node.js crypto Library for Handling Passwords, use Bcrypt

bcrypt provides hash and salt functionality. Therefore it’s better for handling secrets than the built-in crypto library. It’s also faster.

We don’t want attackers to be able to brute-force passwords and tokens with dictionary attacks.

Escape HTML, JS and CSS Output

We should escape these kinds of code so that attacks can’t run malicious client-side code with our app. Dedicated libraries can explicitly mark the data as pure content and should never be executed.

Validate Incoming JSON Schemas

JSON schemas should be validated to make sure that the income request payload has valid data. For instance, we can use the jsonschema library to validate the structure and values of the JSON that’s sent.

We can use the jsonschema library as follows with an Express app:

const express = require('express');
const bodyParser = require('body-parser');
const Validator = require('jsonschema').Validator;
const v = new Validator();
const app = express();

const addressSchema = {
  "id": "/SimpleAddress",
  "type": "object",
  "properties": {
    "address": { "type": "string" },
  },
  "required": ["address"]
};

const schema = {
  "id": "/SimplePerson",
  "type": "object",
  "properties": {
    "name": { "type": "string" },
    "address": { "$ref": "/SimpleAddress" },
  },
  "required": ["name", "address"]
};

v.addSchema(addressSchema, '/SimpleAddress');
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: true }));

app.post('/person', (req, res) => {
  if (v.validate(req.body, schema).errors.length) {
    return res.send(400)
  }
  res.send('success');
});

app.listen(3000, () => console.log('server started'));

In the code above, we required the jsonschema library and use its validator. Then we defined the /SimpleAddress schema, which is referenced by the /SimplePerson schema.

We add the /SimpleAddress schema with:

v.addSchema(addressSchema, '/SimpleAddress');

to reference it in /SimplePerson .

Then we can check our request body against our schema with:

v.validate(req.body, schema).errors.length

Then we stop the request from proceeding if the request body fails validation.

Support blacklisting JWTs

JSON Web Tokens (JWTs) that were used for malicious user activity should be revoked. Therefore, our app needs a way to revoke these tokens.

Conclusion

We should secure our app by checking for vulnerabilities and revoking tokens that were used for malicious purposes. Also, we need to take steps to prevent malicious from running on client and server-side by sanitizing data everywhere.

Finally, we should validate request bodies to make sure that valid data is submitted to our app.

Categories
Node.js Best Practices

Node.js Best Practices — Error Handling

Node.js is a popular runtime to write apps for. These apps are often production quality apps that are used by many people. To make maintaining them easier, we have to set some guidelines for people to follow.

In this article, we’ll look at the best practices for error handling in Node.js apps.

Use only the built-in Error object

In JavaScript, we can create a subclass of the error object, so there’s no need to create a custom type or return some error value to throw errors.

We can either instantiate the Error class or a subclass of the Error class. This way, it’ll increase uniformity and decrease confusion. It’s much less headache for everyone reading the code.

For example, we can throw an Error instance by writing:

throw new Error('error');

We can catch errors and handle them gracefully by using the catch block as follows:

try {
  foo();
} catch (e) {
  if (e instanceof RangeError) {
    console.error(`${e.name} - ${e.message}`)
  }
}

The code above only handles RangeError instances.

We can get the error name and stack trace from the Error instance. Also, we can create our own error class by subclassing the Error class:

class CustomError extends Error {
  constructor(foo = 'bar', ...params) {
    super(...params)
    if (Error.captureStackTrace) {
      Error.captureStackTrace(this, CustomError)
    }

    this.name = 'CustomError'
    this.foo = foo
    this.date = new Date()
  }
}

Then we can throw a CustomError instance and catch it by writing:

try {
  throw new CustomError('baz');
} catch (e) {
  if (e instanceof CustomError) {
    console.error(`${e.name} - ${e.foo}`)
  }
}

Distinguish Operational vs Programmer Errors

Operational errors by the user should be handled by the user. For instance, if invalid input is entered, we should handle it by sending them an error message.

On the other hand, if the error is a programmer error, then we should fix them. Keeping the programmer error unfixed isn’t a good idea since it leads to bad user experience and data corruption.

For user errors, we should send the client 400 series response codes for web apps. 400 is for bad input, 401 for unauthorized, and 403 for forbidden.

Handle Errors Centrally, Not within an Express Middleware

In Express apps, error handling should be done centrally in its own middleware rather than spread out in multiple middlewares.

For instance, we should write something like the following code to handle errors:

const express = require('express');
const bodyParser = require('body-parser');

const app = express();

app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: true }));

app.get('/', (req, res, next) => {
  try {
    throw new Error('error')
    res.send('hello')
  } catch (err) {
    next(err)
  }
});

app.use((err, req, res, next) => {
  res.send('error occurred')
})

app.listen(3000, () => console.log('server started'));

In the code above, we added an error handler to the end of the app so that it’ll run when all routes call next .

This way, we don’t have to write multiple pieces of code to handle errors. In the code above:

app.use((err, req, res, next) => {
  res.send('error occurred')
})

is the error handler middleware. The next function in the GET route calls the error handler since it threw an error.

Document API errors using Swagger

Documentation is very important since it lets people know how to use the API and make sure that changes to the API won’t accidentally create undesirable behavior.

For instance, adding Swagger to an Express app is easy since we have the Swagger UI Express package to do it for us. We can install the Swagger UI Express package by writing:

npm install swagger-ui-express

Then we can create a swagger.json file as follows:

{
  "openapi": "3.0.0",
  "info": {
    "title": "Sample API",
    "description": "Optional multiline or single-line description in [CommonMark](http://commonmark.org/help/) or HTML.",
    "version": "0.1.9"
  },
  "servers": [
    {
      "url": "https://UnwittingRudeObservation--five-nine.repl.co",
      "description": "Optional server description, e.g. Main (production) server"
    },
    {
      "url": "https://UnwittingRudeObservation--five-nine.repl.co",
      "description": "Optional server description, e.g. Internal staging server for testing"
    }
  ],
  "paths": {
    "/": {
      "get": {
        "summary": "Returns a hello message.",
        "description": "Optional extended description in CommonMark or HTML.",
        "responses": {
          "200": {
            "description": "A JSON string message",
            "content": {
              "application/json": {
                "schema": {
                  "type": "string",
                  "items": {
                    "type": "string"
                  }
                }
              }
            }
          }
        }
      }
    }
  }
}

Then in our app, we can write:

const express = require('express');
const bodyParser = require('body-parser');
const swaggerUi = require('swagger-ui-express');
const swaggerDocument = require('./swagger.json');
const app = express();

app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: true }));

app.get('/', (req, res, next) => {
  res.send('hello')
});

app.use('/api-docs', swaggerUi.serve, swaggerUi.setup(swaggerDocument));
app.listen(3000, () => console.log('server started'));

We can edit our Swagger API documentation by going to https://editor.swagger.io/.

Conclusion

If we want to throw errors in our Node app, we should throw an instance of Error or subclasses of Error . Catching errors can be done in the catch block for Node apps. It should also be handled by a central error handler for Express apps.

To make using and changing the API easy, we should use Swagger to document the routes that our API has. For Express apps, we can use Swagger UI Express to serve our swagger.json file in an easy to read format as a separate page.

For user errors, we should send errors back to them. Programmer errors should be fixed by us.

Categories
Node.js Best Practices

Node.js Best Practices — Testing and Quality

Node.js is a popular runtime to write apps for. These apps are often production quality apps that are used by many people. To make maintaining them easier, we have to set some guidelines for people to follow.

In this article, we’ll look at testing and maintaining the quality of Node.js code.

Write API (Component) Tests at Least

We should write tests as soon as we have time to do it. Tests prevent regressions by checking that existing functionality is still working. That way, we don’t have to worry about our code changes breaking any critical functionality.

It’s easy to create tests for our app. We can use test frameworks like Jest and libraries like Superagent to test our APIs.

Also, we should make sure that code coverage is high so that we’re actually testing most of our code with our tests.

For instance, we can add tests easily to an Express app with Jest and Supertest by running:

npm i jest supertest

Then we write the following:

index.js :

const express = require('express');
const bodyParser = require('body-parser');
const app = express();
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: true }));

app.get('/', (req, res, next) => {
  res.json({hello: 'hello'});
});

module.exports = app;

index.test.js :

const request = require('supertest');
const app = require('./index');

describe('hello test', () => {
  it('/ should return hello response', async () => {
    const res = await request(app)
      .get('/')
    expect(res.statusCode).toEqual(200)
    expect(res.body).toEqual({hello: 'hello'})
  })
})

In the code file, we added supertest to our index.test.js test file. Then we test our route by making a request to the / route and checking the response code and body.

The toEqual method checks for deep equality so that we can check for any value.

Include 3 Parts in Each Test Name

Each test name should include 3 parts so that people reading the test cases know what’s being tested. A test name should include what is being tested, what scenarios, and what’s the expected result.

Structure Tests by the AAA Pattern

AAA stands for Arrange, Act, and Assert. The first part of the test should set up the data for our tests. Then we actually run the code, and then assert that the returned result is what’s expected.

With these kinds of tests, we understand the main code just by looking at the tests.

Detect Code Issues with a Linter

We should use a code linter to check for basic quality issues with our code. Spacing, formatting, and syntax errors are within its domain. It also checks for common antipatterns to make sure that our code don’t have them. Linters with Node add-ons can also check for security issues with our code.

It’s easy to overlook them without a linter. So we may be running bad quality and code with security vulnerabilities in our code.

Avoid Global Test Fixtures and Seeds

Each test should have its own test fixtures and seeds so that they run independently without anything else. This is important because we need tests to be testing things without depending on anything external. It makes tests easy to add and debug.

It reduces lots of headaches with tests. They shouldn’t depend on any external dependencies.

Inspect for Vulnerable Dependencies

We can use npm audit or snyk.io to check for vulnerable dependencies so that we update those packages as quickly as possible.

With these automated tools, we can check for vulnerable packages without doing anything ourselves.

Tagging Tests

Tagging tests let us search for them easily. We can add the tags to the test names so that we can find them easily.

Check Test Coverage

Test coverage lets us check if tests are running enough parts of our code. Code coverage tools provide highlights to see which parts of the code have been run by tests and what hasn’t. Then we can look at what kinds of code that we need to run with our tests to increase test coverage of our code.

We may also want to fail the build if test coverage falls below a certain threshold.

Inspect for Outdated Packages

We can check for outdated packages with npm outdated and npm-check-updates to detect outdated packages. We can also run it in our CI pipeline to prevent the build from succeeding if we have outdated packages.

This way, we won’t be using outdated packages in our app.

Conclusion

Adding tests is easy with Node apps. It lets us check for regressions without much effort. When we divide our tests to test small pieces of the code, then adding tests takes little effort. This is especially easy with test frameworks like Jest and test HTTP clients like Superagent.

We should also check for vulnerable packages and update them as soon as possible. In addition, we should check our own code for security vulnerabilities and fix them as soon as possible.

Categories
Node.js Best Practices

Node.js Best Practices — Using Modern Features

Node.js is a popular runtime to write apps. These apps are often production quality apps that are used by many people. To make maintaining them easier, we’ve to set some guidelines for people to follow.

In this article, we’ll look at some modern JavaScript features that we should use to create code that’s clean and easy to maintain.

Prefer const over let. Ditch the var

var is an outdated keyword for creating variables that should never be used again. The scope is inconsistent unlike let and const . var is function scoped, so that it can be accessed from outside blocks and create potential issues with our code.

let and const are blocked scoped so they can’t be accessed outside a block. const prevents reassignment of the constant to another value.

For example, if we have the following code:

var callbacks = [];
(function() {
  for (var i = 0; i < 5; i++) {
    callbacks.push( function() { return i; } );
  }
})();

console.log(callbacks.map( function(cb) { return cb(); } ));

Then we’ll see [ 5, 5, 5, 5, 5 ] as the value of callbacks.map( function(cb) { return cb(); } ).

This is because i ‘s value isn’t passed into the callback until it reaches 5. Then we run each of them with the value 5. The code above is actually the same as:

var callbacks = [];
(function() {
  var i
  for (i = 0; i < 5; i++) {
    callbacks.push( function() { return i; } );
  }
})();
console.log(callbacks.map( function(cb) { return cb(); } ));

because of hoisting. Therefore, the value of i would be 5 when the callbacks are run.

let variables don’t host, so we won’t have the same issue. Therefore, we won’t have the same issue:

var callbacks = [];
(function() {
  for (let i = 0; i < 5; i++) {
    callbacks.push( function() { return i; } );
  }
})();
console.log(callbacks.map( function(cb) { return cb(); } ));

As we can see, var is a pain and it’s confusing, so we should never use it.

Require Modules First, Not Inside Functions

We should require modules on the top of each code file. This lets us easily tell which dependencies are required.

Require runs synchronously in Node.js. Therefore, if they’re called within a function, it may block other pieces of code from running at a more critical time.

If any required module or dependencies throw an error and crash the server, it’s better to find out earlier.

Require Modules by Folders, as Opposed to the Files Directly

We should require modules by folders instead of the files directly. This is because we don’t want to break the require expressions in our user’s apps if we change the folder structure of our module.

Therefore, the following is good:

require('./foo');

But the following is bad:

require('./bar/foo');

Use the === Operator

The strict equality operator === is better than the == operator because it doesn’t coerce the types of the variables before comparing them. With the === operator, both operands must have the same type for them to be equal.

This is good because it prevents lots of errors when comparing things. Using the == operator is bad because expressions like the following all return true:

null == undefined
false == '0'
0 == ''
0 == '0'

In many cases, it’s not what we want. There’re also many other strange edge cases that may cause errors in our app if we use the == operator. Therefore, we should use the === operator.

Use Async Await and Avoid Callbacks

Since Node 8 LTS, async and await is a feature in Node. Therefore, we should use it to chain promises whenever possible. It’s a great shorthand to for chaining promises.

The old callback APIs are slowly converted into promises API in cord Node modules like fs . Therefore, now we can use async and await in places other than our own code.

To handle errors in async and await , we can handle errors as follows:

(async ()=>{
  try {
    await Promise.reject('error')
  }
  catch(ex){
    console.log(ex);
  }
})();

In the code above, we catch the error with the catch block and log the value of ex , which should be 'error' from the Promise.reject .

Conclusion

New constructs in JavaScript are there because they’re good. They make code shorter and cleaner. They make the code pleasant to read and change. It just makes everyone happy to code in JavaScript. Old constructs like var should be eliminated from all code. === should be used instead of == .

Categories
Node.js Best Practices

Node.js Best Practices — Validation and Code Style

Node.js is a popular runtime to write apps for. These apps are often production quality apps that are used by many people. To make maintaining them easier, we’ve to set some guidelines for people to follow.

In this article, we’ll look at input validation and writing code that has a good style.

Validate Arguments Using a Dedicated Library

We should validate inputs in our app so that users can’t submit data that are invalid and cause data corruption.

For express apps, we should check that our endpoints are checking for valid inputs. We should check if we aren’t checking already.

For instance, we can use the express-validator package to make form validation easy. To install it, we run:

npm install --save express-validator

Then we can use it as follows:

const express = require('express');
const bodyParser = require('body-parser');
const { check, validationResult } = require('express-validator');

const app = express();
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: true }));

app.post('/', [
  check('email').isEmail(),
], (req, res, next) => {
  const errors = validationResult(req);
  if (!errors.isEmpty()) {
    return res.status(422).json({ errors: errors.array() });
  }
  const { email } = req.body;
  res.send(email);
});

app.listen(3000, () => console.log('server started'));

In the code above, we have the check(‘email’).isEmail() to check if the email that’s submitted with the body is a valid email.

Then in the route handler, we send an error response back to the user if it’s not a valid email.

Use ESLint

JavaScript is a very forgiving language. This means it lets us do lots of things that people may not like. It’s also very easy to make mistakes with it with its dynamic types and permissive syntax.

Therefore, we should use linter like ESLint to check for possible code errors and fix bad style. It’s useful for identifying small issues like spacing but also checks for serious anti-patterns like throwing errors without classification.

It can also be used in conjunction with Prettier and Beautify to format code in an easy to read way,

Node.js Specific Plugins for ESLint

There’re specific ESLint plugins for Node like eslint-plugin-node , eslint-plugin-mocha , and eslint-plugin-node-security .

They check for Node specific errors and possible security issues in our code so that our apps won’t be susceptible to them.

Start a Codeblock’s Curly Braces on the Same Line

We should start curly braces on the same line for blocks. For instance, if we define a function, we write:

function foo() {
  // code block
}

instead of:

function foo()
{
  // code block
}

Separate Statements Properly

We should use semicolons to separate our statements and we should use line breaks properly to make sure that our code is easy to read. They both help eliminate regular syntax errors.

ESLint, Prettier, and Standardjs can automatically resolve these issues.

We shouldn’t let the JavaScript interpreter insert semicolons automatically. It’s an easy way to create unexpected errors.

Examples of properly separated states include:

function foo() {
  // code block
}

foo();

or:

const items = [1, 2, 3];
items.forEach(console.log);

The following example is a bad example that’ll throw an error:

const count = 1
(function foo() {

}())

We’ll get 1 is not a function since it’s trying to run 1 as a function. To avoid the error, we should put a semicolon after the 1 :

const count = 1;
(function foo() {

}())

Name Our Functions

We should name our functions so that we can debug them easily. Anonymous functions have no name, so it’s hard to trace them. Named functions are easier to understand what we’re looking at when checking a memory snapshot.

Use Naming Conventions for Variables, Constants, Functions, and Classes

The lower camel case is the convention for constants, variables, and function names. Upper camel case is used when naming classes. This will help us distinguish between plain variables and functions, and classes that require instantiation.

Also, we should use descriptive names, but keep them short.

JavaScript allows invoking a constructor without instantiating it, so it’s important to distinguish by the case so that we don’t confuse constructor functions with regular functions.

For example, we should write:

function Person(name){
  this.name = name;
}

function fooBar(){

}

for functions, and name variables as follows:

let fooBar = 1;

Conclusion

We should name things according to convention and lint our code to prevent bad styles and syntax errors.

Also, we should validate our inputs to prevent data corruption and other errors.