How to validate and sanitize user input in JavaScript
What’s the best way to validate user inputs in JavaScript or Node.JS?
There’s a couple validator modules that I like to use
There are a lot of input validation libraries out there. But to me, the 2 listed above provide the easiest API.
If I’m looking for a sanitizing library, I’ll go with DOMPurify library or validator.js.
Now, don’t mix sanitizing with validating.
const isNotSame = validating !== sanitizing; // true
They’re not the same at all.
What is input validation
Input validation is like running tests about the data the user is filling out in a form.
If they’re is an email field, you want to make sure that it’s not empty, and that it follows a specific email format pattern.
If the form has a name field, you want to make sure that it’s not empty, it’s a string, and that it meets a minimum length requirement.
These tests are helpful to let the user know that the data they’ve entered is correct or incorrect.
If it’s incorrect you can send them a message to correct it.
Validating user input values can happen on the client side for a better user experience, and it should happen in the back-end side as well.
People can bypass the client-side code and send wrongly formatted data to the back-end. So validate in the back-end code as well.
What is output sanitizing
Sanitizing data is best done before displaying the data to the user screen.
It cleanses the original data to prevent it from exploiting any security holes in your application.
I don’t recommend doing input sanitizing because you may risk altering the data in ways that make it unusable.
Validating input: Okay solution
validator.js is a great and easy input validation module to use.
It can be used in Node.js and client-side JavaScript.
import validator from 'validator'; // 62.8k size
const data = {
name: 'Ruben',
about: 'I like long walks in the beach.',
email: 'test@mail.com',
};
// Check if name or about field is empty
if (validator.isEmpty(data.name) || validator.isEmpty(data.about)) {
console.log('Name or about field is empty')
}
// Check if email format is correct
if (!validator.isEmail(data.email)) {
console.log('Email format is incorrect')
}
The caveat with this library is that it only validates strings.
If you don’t know your input is going to be a string value than I’d suggest to convert it into a string with a template literal such as backtick quotes.
const age = 20;
console.log(typeof `${age}`); // string
If you don’t support ES6 features, than try this method:
const age = 20;
console.log(age + ''); // '20'
This module is 62.8K and can be reduced to 16.2k if it’s gzipped.
You can also just import modules you need instead of the whole library.
import isEmpty from 'validator/lib/isEmpty'; // 2.2k
import isEmail from 'validator/lib/isEmail'; // 6.9k
In my opinion, I believe validator.js is really nice, but there is better.
Validating input: Best solution
Yup is a light weight JavaScript schema builder. Yup also parses data, and validates it.
Building a schema is extremely simple. Let’s build a schema for the data object that was used above.
First we’re going to create a variable called schema
and begin defining the keys of our object.
const schema = yup.object().shape();
Now I will pass my object inside the yup.object().shape()
function.
const schema = yup.object()
.shape({
name: yup.string().required().min(1, 'Please enter a name'),
about: yup.string()
.min(10, 'Enter more than 9 characters')
.max(160, 'Cannot be more than 160 characters'),
email: yup.string().email('Please enter a valid email address'),
});
What I like about Yup is that you can enter custom messages for every test.
Here’s how to test your Yup schema:
const data = {
name: 'Ruben',
about: 'I like long walks in the beach.',
email: 'test@mail.com',
};
schema.validate(data)
.then(data => console.log(data))
.catch(err => console.log(err));
It is an asynchronous process, but they have function utilities to make synchronous. I’d recommend doing asynchronous as much as possible.
Output sanitizing: Okay solution
Most template languages (Pug, JSX, etc) have output sanitizing built in by default.
But if you don’t you can use validator.js to clean up a dirty string.
const dirty = `I love to do evil <img src="http://unsplash.it/100/100?random" onload="alert('you got hacked');" />`;
const clean = validator.escape(dirty);
validator.escape()
will convert HTML tags into HTML entities.
Output sanitizing: Better solution
I like to use DOMPurify
as my main option to sanitize data. I believe it does a much better job.
If you grab the same variable, dirty
, and cleanse it with DOMPurify
the output would look like
import DOMPurify from 'dompurify';
const clean = DOMPurify.sanitize(dirty);
// I love to do evil <img src="http://unsplash.it/100/100?random">
It leaves the the HTML element, img
, but it removes any funky HTML attributes.
Let’s dirty up the string a bit more and see what happens.
const dirty = `I love to do evil <img src="http://unsplash.it/100/100?random" onload="alert('you got hacked');" /> <script>alert("YOU GOT HACKED!");`;
const clean = DOMPurify.sanitize(dirty);
// I love to do evil <img src="http://unsplash.it/100/100?random">
DOMPurify removes any script HTML elements and its content.
If you must do input sanitizing
Again, sanitizing really depends on the context of the data. There are cases where sanitizing input is a must.
To sanitize the users input data you can still use validator.js as I demonstrated above.
Validator.js is supported with both client-side and back-end code.
If you want to make DOMPurify work with Node.js, you’ll have to install an extra NPM module to make it work.
You can check if DOMPurify is supported in your environment by running this if
conditional.
if (DOMPurify.isSupported) {
// ...DO some stuff
}
Here’s the full code to making DOMPurify work in Node.js. Make sure to install jsdom.
npm i -S jsdom dompurify
import createDOMPurify from 'dompurify';
import { JSDOM } from 'jsdom';
import createDOMPurify from 'dompurify';
import { JSDOM } from 'jsdom';
const dirty = `I love to do evil <img src="http://unsplash.it/100/100?random" onload="alert('you got hacked');" /> <script>alert('you got hacked!')</script>`
const windowEmulator = new JSDOM('').window;
const DOMPurify = createDOMPurify(windowEmulator);
if (DOMPurify.isSupported) {
const clean = DOMPurify.sanitize(dirty)
console.log(clean);
// I love to do evil <img src="http://unsplash.it/100/100?random">
}
I like to tweet about JavaScript and post helpful code snippets. Follow me there if you would like some too!