43

I validated my Node.js inputs so that they won't be empty, but I want to sanitize them too. Please help me how I can do this.

req.checkBody('name', 'Name is required!').notEmpty();
req.checkBody('surname', 'Surname is required!').notEmpty();
req.checkBody('username', 'Username is required!').notEmpty();
req.checkBody('password', 'Password is required!').notEmpty();
req.checkBody('password2', 'Passwords do not match!').equals(req.body.password);

var errors = req.validationErrors();

if (errors) {
    res.render('user/register', {
        errors: errors,
        user: null,
        title: 'Register'
    });
}
else {
    var userData = {
        name : req.body.name,
        surname : req.body.surname,
        username : req.body.username,
        password : req.body.password,
        avatar : 'No_person.jpg'
    };
    userController.addUser(req,res,userData);
}
kgangadhar
  • 4,886
  • 5
  • 36
  • 54
V.Aleksanyan
  • 577
  • 1
  • 4
  • 7

5 Answers5

57
  • For most of the framework, you can use sanitize node module:

     npm install sanitize --save
    

    And then can use like:

     var sanitizer = require('sanitize')();
    
     var name = sanitizer.value(req.name, 'string');
     var surname= sanitizer.value(req.surname, 'string');
    

    For more can go through sanitize documentation

  • If you are using express, then you can validate and sanitize using express-validator and express-sanitize-input packages as follows:

     const express = require('express');
     const { check } = require('express-validator');
     const app = express();
    
     app.use(express.json())
    
     app.post('/form', [
       check('name').isLength({ min: 3 }).trim().escape(),
       check('email').isEmail().normalizeEmail(),
       check('age').isNumeric().trim().escape()
     ], (req, res) => {
       const name  = req.body.name
       const email = req.body.email
       const age   = req.body.age
     })  
    

    For more can go through express-validator and express-sanitize-input documentation.

  • If you are using Hapi, then you can validate and sanitize using Joi, With the Joi, you can sanitize variable with additional options

     validate(value, schema, {escapeHtml: true}, [callback])
    

    For more can go through Joi documentation.

  • If you don't want to use any third party module and want to sanitize using the built-in node. you can try following:

     // For string variables
     str = typeof(str) === 'string' && str.trim().length > 0 ? str.trim() : '';
     // for boolean values
     bool = typeof(bool) === 'boolean' && bool === true ? true : false;
     // for array values
     arr = typeof(arr) === 'object' && arr instanceof Array ? arr : [];
     // for number values
     num = typeof(num) === 'number' && num % 1 === 0 ? num : 0;
     // for objects
     obj = typeof(obj) === 'object' && !(obj instanceof Array) && obj !== null ? obj : {};
    
kgangadhar
  • 4,886
  • 5
  • 36
  • 54
  • @V.Aleksanyan, Here `string` refers to the value you want to match or the regex expression u want to allow. you can try out this in [`sanitizer runkit`](https://npm.runkit.com/sanitize) – kgangadhar Oct 13 '17 at 18:01
  • WHen I submit form I check validation and write your code var name = sanitizer.value(req.body.name, 'string'); var name = sanitizer.value(req.body.surname, 'string'); var name = sanitizer.value(req.body.username, 'string'); It returns me this error " string is not a valid sanitizer type ". Please say me Why is it? – V.Aleksanyan Oct 13 '17 at 20:02
  • 1
    That's just an example to show how it works, It should be like this `var myMatchingString = sanitizer.value(req.mystring, /abc123/);` or a regular expression instead of `abc123`, go through [documentaion](https://github.com/pocketly/node-sanitize), it will explain how to use as a middleware with application – kgangadhar Oct 13 '17 at 20:08
  • `check()` isn't built-in with Express. It's part of [express-validator](https://express-validator.github.io/docs/check-api.html#checkfield-message) – sloreti Mar 23 '21 at 20:06
  • getting "string is not a valid sanitizer type" for var name = sanitizer.value(req.name, 'string'); – abhinav1602 Oct 11 '22 at 07:21
10

Actually, I wrote a package to solve this problem easily. You can use it or contribute to it on Github.

Download this package from here: https://www.npmjs.com/package/string-sanitizer

You can use this utility package to sanitize even foreign languages other than English. Under the hood, regex is used in this library. You can convert your string to URL or filename friendly string. The use cases are given below

var string = require("string-sanitizer");

string.sanitize("a.bc@d efg#h"); // abcdefgh
string.sanitize.keepSpace("a.bc@d efg#h"); // abcd efgh
string.sanitize.keepUnicode("a.bc@d efg#hক"); // abcd efghক
string.sanitize.addFullstop("a.bc@d efg#h"); // abcd.efgh
string.sanitize.addUnderscore("a.bc@d efg#h"); // abcd_efgh
string.sanitize.addDash("a.bc@d efg#h"); // abcd-efgh
string.sanitize.removeNumber("@abcd efgh123"); // abcdefgh
string.sanitize.keepNumber("@abcd efgh123"); // abcdefgh123
string.addFullstop("abcd efgh"); // abcd.efgh
string.addUnderscore("@abcd efgh"); // @abcd_efgh
string.addDash("@abcd efgh"); // @abcd-efgh
string.removeSpace("@abcd efgh"); // @abcdefgh

Codeblock

enter image description here

Hien Nguyen
  • 24,551
  • 7
  • 52
  • 62
Md Fazlul Karim
  • 355
  • 7
  • 15
  • 11
    This library is more akin to a string manipulation library rather than a viable sanitizer. Sanitation should seek to contain the input and preferably be context aware, rather just manipulating a string to some assumed desired result: `string.sanitize("1@2.com")` would ideally not return `"12com"`. Some improvements could include focusing on context specific sanitation (`sanitize.HTMLsafe()`, `sanitize.SQLsafe()`, `sanitize.EmailAddress()`, etc) as well as dropping string helper functions (leave the kitchen sink at home). – PotatoFarmer Feb 12 '20 at 07:15
  • 8
    Thank you for your suggestion. Basically, I wrote it to solve a practical problem. Since then many people find it handy and useful. "Sanitizer" means cleaning or disinfecting in our practical life. The word "sanitizer" is used in that practical sense. Nothing more. Besides, the name string-manipulator and string-manipulation both were already taken while publishing this library. I will try to add these updates in the future. Thank you, again. – Md Fazlul Karim Feb 13 '20 at 19:23
  • 1
    So far i see this as the best approach to start solving the sanitize problem. i dont want to know what the input is. just like the way js can detect an interger and a string thats how i think it should work and this is somewhat close. The issue is to avoid sql injection by triming unwanted symbols added before or after the input value. – Jerubaal Xerxes May 31 '22 at 12:59
0

validator has 5M downloads/week and seems like the most popular package in the industry as of today. express-validator uses validator as it's core. These are certainly one option, as are other packages like xss and sanitize-html

There is extensive documentation on both of the validator packages, here is the section on sanitization:

https://express-validator.github.io/docs/sanitization.html

Dmitri R117
  • 2,502
  • 23
  • 20
0

I use Yup for validation. It's a lot smaller package than Joi. I've used Yup on both front and backend and I usually throw my validations into a shared npm package that both the front end and backend projects can have the same validation library

npm install -S yup

then

import * as yup from 'yup';

let schema = yup.object().shape({
  name: yup.string().required(),
  age: yup.number().required().positive().integer(),
  email: yup.string().email(),
  website: yup.string().url(),
  createdOn: yup.date().default(function () {
    return new Date();
  }),
});

// check validity
schema
  .isValid({
    name: 'jimmy',
    age: 24,
  })
  .then(function (valid) {
    valid; // => true
  });

From Yup's website:

Yup is a JavaScript schema builder for value parsing and validation. Define a schema, transform a value to match, validate the shape of an existing value, or both. Yup schema are extremely expressive and allow modeling complex, interdependent validations, or value transformations.

Yup's API is heavily inspired by Joi, but leaner and built with client-side validation as its primary use-case. Yup separates the parsing and validating functions into separate steps. cast() transforms data while validate checks that the input is the correct shape. Each can be performed together (such as HTML form validation) or separately (such as deserializing trusted data from APIs).

You will want to create your own validators for special items like password matching etc. This can be done using regex then add the function to Yup like so:

let schema = string().matches(/(hi|bye)/);

Put all your validation functions into your shared NPM package (including your typescript types etc). The cases where your frontend team is out of sync with validations on the backend will now be less of a concern.

Richard Torcato
  • 2,504
  • 25
  • 26
0

You can create custom middleware that you can integrate into any endpoint (or globally for every request), and it will check if any of the inputs in the request body is problematic.

It will check if:

  • The input contains some embedded HTML code (scripts of images for example).
  • First non-white character is =, which make Excel imports vulnerable.

Middleware

const containsHtml = /<[^>]*>/;
const startsWithEqual = /^\s*=/;

const isInvalidInput = (input) => {
  return containsHtml.test(input) || startsWithEqual.test(input);
}

exports.inputValidator = (req,res,next) => {
  let invalid_input_count = 0;
  const obj = req.body;
  const stack = [obj];
  while (stack?.length > 0) {
    const currentObj = stack.pop();
    Object.keys(currentObj).forEach(key => {
      if (typeof currentObj[key] === 'object' && currentObj[key] !== null) {
        stack.push(currentObj[key]);
      } else {
        if (typeof currentObj[key] === 'string' && isInvalidInput(currentObj[key])) {
          invalid_input_count++;
        }
      }
    });
  }

  if(invalid_input_count === 0) {
    return next();
  } else{
    return res.status(400).json({ success: false, error_code: 'invalid_input'});
  }
}

Usage

const express = require('express');
const { inputValidator } = require('./util/input-validator');

...

const app = express();
app.use(inputValidator); // Check every request for vulnerable inputs 
NeNaD
  • 18,172
  • 8
  • 47
  • 89