How to sanitize inputs in Node.js
Input sanitization removes or encodes potentially dangerous characters from user input to prevent security vulnerabilities like XSS, SQL injection, and command injection. As the creator of CoreUI with 12 years of Node.js development experience, I’ve secured applications serving millions of users by implementing proper input sanitization strategies that block malicious payloads while preserving legitimate user data.
The most reliable approach combines validation libraries with context-specific sanitization.
Sanitize HTML with DOMPurify
npm install isomorphic-dompurify
const createDOMPurify = require('isomorphic-dompurify')
const DOMPurify = createDOMPurify()
function sanitizeHTML(dirty) {
return DOMPurify.sanitize(dirty)
}
// User input
const userInput = '<script>alert("XSS")</script><p>Hello</p>'
// Sanitized output
const clean = sanitizeHTML(userInput)
console.log(clean) // <p>Hello</p>
Escape Special Characters
function escapeHtml(text) {
const map = {
'&': '&',
'<': '<',
'>': '>',
'"': '"',
"'": '''
}
return text.replace(/[&<>"']/g, char => map[char])
}
const userInput = '<script>alert("XSS")</script>'
const escaped = escapeHtml(userInput)
console.log(escaped)
// <script>alert("XSS")</script>
Sanitize with validator.js
npm install validator
const validator = require('validator')
// Email sanitization
const email = ' [email protected] '
const sanitizedEmail = validator.normalizeEmail(email)
console.log(sanitizedEmail) // [email protected]
// Trim and escape
const username = ' <script>admin</script> '
const sanitizedUsername = validator.trim(validator.escape(username))
console.log(sanitizedUsername) // <script>admin</script>
// Remove non-alphanumeric
const slug = 'Hello World! @#$'
const sanitizedSlug = slug.toLowerCase()
.replace(/[^a-z0-9]+/g, '-')
.replace(/^-|-$/g, '')
console.log(sanitizedSlug) // hello-world
Prevent SQL Injection with Parameterized Queries
const mysql = require('mysql2/promise')
// BAD - Vulnerable to SQL injection
async function getUserBad(userId) {
const query = `SELECT * FROM users WHERE id = ${userId}`
const [rows] = await pool.query(query)
return rows[0]
}
// GOOD - Parameterized query
async function getUserGood(userId) {
const query = 'SELECT * FROM users WHERE id = ?'
const [rows] = await pool.query(query, [userId])
return rows[0]
}
// Named parameters with object
async function findUser(filters) {
const query = 'SELECT * FROM users WHERE email = :email AND active = :active'
const [rows] = await pool.query(query, {
email: filters.email,
active: filters.active
})
return rows[0]
}
Sanitize File Paths
const path = require('path')
function sanitizeFilePath(userPath) {
// Remove directory traversal attempts
const normalized = path.normalize(userPath).replace(/^(\.\.[\/\\])+/, '')
// Remove absolute paths
const relative = normalized.replace(/^[\/\\]+/, '')
// Whitelist allowed characters
const safe = relative.replace(/[^a-zA-Z0-9_\-\.\/]/g, '')
return safe
}
// Test cases
console.log(sanitizeFilePath('../../../etc/passwd')) // etcpasswd
console.log(sanitizeFilePath('/etc/passwd')) // etcpasswd
console.log(sanitizeFilePath('user/documents/file.txt')) // user/documents/file.txt
console.log(sanitizeFilePath('file<script>.txt')) // filescript.txt
Sanitize MongoDB Queries
const mongoose = require('mongoose')
// BAD - Vulnerable to NoSQL injection
async function findUserBad(req) {
const user = await User.findOne({ username: req.body.username })
return user
}
// GOOD - Validate and sanitize
function sanitizeMongoString(value) {
if (typeof value !== 'string') {
throw new Error('Expected string')
}
// Remove MongoDB operators
return value.replace(/^\$/, '')
}
async function findUserGood(req) {
const username = sanitizeMongoString(req.body.username)
const user = await User.findOne({ username })
return user
}
// Better - Use schema validation
const userSchema = new mongoose.Schema({
username: {
type: String,
required: true,
match: /^[a-zA-Z0-9_]+$/
}
})
Express Middleware for Input Sanitization
const express = require('express')
const validator = require('validator')
const app = express()
app.use(express.json())
// Sanitize middleware
function sanitizeInputs(req, res, next) {
if (req.body) {
Object.keys(req.body).forEach(key => {
if (typeof req.body[key] === 'string') {
// Trim whitespace
req.body[key] = validator.trim(req.body[key])
// Escape HTML
req.body[key] = validator.escape(req.body[key])
}
})
}
next()
}
app.use(sanitizeInputs)
app.post('/api/users', (req, res) => {
// req.body is already sanitized
const { name, email } = req.body
res.json({ name, email })
})
Comprehensive Sanitization Function
const validator = require('validator')
function sanitizeInput(input, options = {}) {
const {
type = 'text',
allowHTML = false,
maxLength = 10000
} = options
if (typeof input !== 'string') {
return ''
}
// Trim whitespace
let sanitized = validator.trim(input)
// Enforce max length
sanitized = sanitized.slice(0, maxLength)
// Type-specific sanitization
switch (type) {
case 'email':
sanitized = validator.normalizeEmail(sanitized) || ''
if (!validator.isEmail(sanitized)) {
return ''
}
break
case 'url':
if (!validator.isURL(sanitized)) {
return ''
}
break
case 'number':
if (!validator.isNumeric(sanitized)) {
return ''
}
break
case 'alphanumeric':
sanitized = sanitized.replace(/[^a-zA-Z0-9]/g, '')
break
case 'html':
if (allowHTML) {
const DOMPurify = require('isomorphic-dompurify')()
sanitized = DOMPurify.sanitize(sanitized)
} else {
sanitized = validator.escape(sanitized)
}
break
case 'text':
default:
sanitized = validator.escape(sanitized)
break
}
return sanitized
}
// Usage
console.log(sanitizeInput(' [email protected] ', { type: 'email' }))
// [email protected]
console.log(sanitizeInput('<script>alert("xss")</script>', { type: 'text' }))
// <script>alert("xss")</script>
console.log(sanitizeInput('<b>Bold</b> text', { type: 'html', allowHTML: true }))
// <b>Bold</b> text
Best Practice Note
This is the input sanitization strategy we use across all CoreUI Node.js applications to prevent security vulnerabilities. Always sanitize user input at the point of entry, use parameterized queries for database operations, validate data types before sanitization, and apply context-specific sanitization (HTML escaping for display, SQL parameters for queries, path sanitization for file operations). Remember that sanitization is not a replacement for proper validation - always validate first, then sanitize.
For production applications, consider using CoreUI’s Node.js Admin Template which includes pre-configured security middleware.
Related Articles
For complete security implementation, check out how to validate data in Node.js and how to prevent XSS attacks in Node.js.



