Input Validation
Input Validation
Client-side Validation
// HTML5 validation
<input type="email" required />
<input type="number" min="0" max="100" />
<input type="text" pattern="[A-Za-z]{3}" />
// React validation
function validateEmail(email) {
const re = /^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$/;
return re.test(email);
}
// Zod schema
import { z } from 'zod';
const userSchema = z.object({
name: z.string().min(1).max(100),
email: z.string().email(),
age: z.number().int().min(0).max(150),
password: z.string().min(8).max(100),
});
Server-side Validation
// Express with Joi
const Joi = require('joi');
const schema = Joi.object({
name: Joi.string().min(1).max(100).required(),
email: Joi.string().email().required(),
age: Joi.number().integer().min(0).max(150),
});
app.post('/user', (req, res) => {
const { error, value } = schema.validate(req.body);
if (error) {
return res.status(400).json({ error: error.details });
}
// Process valid data
});
// Express with express-validator
const { body, validationResult } = require('express-validator');
app.post('/user',
body('email').isEmail(),
body('password').isLength({ min: 8 }),
(req, res) => {
const errors = validationResult(req);
if (!errors.isEmpty()) {
return res.status(400).json({ errors: errors.array() });
}
}
);
Whitelist Validation
// Only allow specific values
function validateStatus(status) {
const allowed = ['active', 'inactive', 'pending'];
return allowed.includes(status);
}
// Only allow specific characters
function validateUsername(username) {
return /^[a-zA-Z0-9_]+$/.test(username);
}
// Only allow specific types
function validateAge(age) {
return Number.isInteger(age) && age >= 0 && age <= 150;
}
Output Encoding
Output Encoding
HTML Entity Encoding
function escapeHtml(str) {
const map = {
'&': '&',
'<': '<',
'>': '>',
'"': '"',
"'": ''',
};
return str.replace(/[&<>"']/g, char => map[char]);
}
// Use in templates
const safeHtml = `<div>${escapeHtml(userInput)}</div>`;
URL Encoding
function encodeUrl(str) {
return encodeURIComponent(str);
}
// Safe URL construction
const safeUrl = `https://example.com/search?q=${encodeUrl(userInput)}`;
// Or use URL API
const url = new URL('https://example.com/search');
url.searchParams.set('q', userInput);
JavaScript String Encoding
function escapeJs(str) {
return str
n .replace(/\\/g, '\\\\\')
.replace(/'/g, "\\\'")
.replace(/"/g, '\\"')
.replace(/\n/g, '\\n')
.replace(/\r/g, '\\r')
.replace(/\t/g, '\\t');
}
// Safe in script tag
const script = `<script>var data = "${escapeJs(userInput)}";</script>`;
CSS Encoding
function escapeCss(str) {
return str.replace(/[^a-zA-Z0-9-]/g, '\\\$&');
}
// Safe in style
const style = `div::before { content: "${escapeCss(userInput)}"; }`;
React Auto-escaping
// React auto-escapes by default
function UserGreeting({ name }) {
return <h1>Hello, {name}</h1>; // Safe
}
// Avoid dangerouslySetInnerHTML
// Only use with sanitized content
import DOMPurify from 'dompurify';
function Comment({ html }) {
const clean = DOMPurify.sanitize(html);
return <div dangerouslySetInnerHTML={{ __html: clean }} />;
}
Sanitization Libraries
Sanitization Libraries
DOMPurify
import DOMPurify from 'dompurify';
// Basic sanitization
const clean = DOMPurify.sanitize(userInput);
// Allow specific tags
const clean = DOMPurify.sanitize(userInput, {
ALLOWED_TAGS: ['b', 'i', 'em', 'strong', 'a'],
ALLOWED_ATTR: ['href'],
});
// For React
import DOMPurify from 'dompurify';
function Comment({ html }) {
const clean = DOMPurify.sanitize(html);
return <div dangerouslySetInnerHTML={{ __html: clean }} />;
}
Validator.js
import validator from 'validator';
// Email
validator.isEmail('test@example.com'); // true
// URL
validator.isURL('https://example.com'); // true
// Alphanumeric
validator.isAlphanumeric('abc123'); // true
// Strong password
validator.isStrongPassword('P@ssw0rd'); // true
// Sanitize
validator.trim(' hello '); // 'hello'
validator.escape('<script>'); // '<script>'
sanitize-html
import sanitizeHtml from 'sanitize-html';
// Basic sanitization
const clean = sanitizeHtml(userInput);
// Allow specific tags
const clean = sanitizeHtml(userInput, {
allowedTags: ['b', 'i', 'em', 'strong', 'a'],
allowedAttributes: {
'a': ['href'],
},
allowedSchemes: ['https', 'mailto'],
});
// Custom allowed tags
const clean = sanitizeHtml(userInput, {
allowedTags: sanitizeHtml.defaults.allowedTags.concat(['img']),
allowedAttributes: {
...sanitizeHtml.defaults.allowedAttributes,
'img': ['src', 'alt', 'title'],
},
});
Best Practices
- Validate on both client and server
- Use whitelist validation
- Sanitize HTML output with DOMPurify
- Encode output based on context
- Use framework auto-escaping
- Don't trust user input
- Test with malicious input
- Use Content Security Policy
Testing Sanitization
describe('Input Sanitization', () => {
it('should sanitize HTML input', () => {
const malicious = '<script>alert(1)</script>';
const clean = DOMPurify.sanitize(malicious);
expect(clean).not.toContain('<script>');
});
it('should validate email format', () => {
expect(validator.isEmail('test@example.com')).toBe(true);
expect(validator.isEmail('invalid')).toBe(false);
});
it('should escape HTML entities', () => {
const input = '<script>alert(1)</script>';
const escaped = escapeHtml(input);
expect(escaped).toContain('<script>');
});
});
Practice Problems
Create a reusable React component implementing Input Sanitization. Include proper state management and accessibility.
Solution
// Production-ready component with:
// - Proper TypeScript types
// - Accessibility (ARIA)
// - Error boundaries
// - Loading states
// - Memoization where neededWrite unit and integration tests for Input Sanitization using React Testing Library.
Solution
// Test coverage:
// 1. Rendering tests
// 2. Interaction tests
// 3. Edge case tests
// 4. Accessibility testsOptimize Input Sanitization for performance. Consider memoization, code splitting, and bundle size.
Solution
// Optimization techniques:
// 1. React.memo / useMemo / useCallback
// 2. Code splitting with lazy()
// 3. Virtual scrolling for lists
// 4. Image lazy loading
// 5. Bundle analysisQuiz
1. What is input sanitization?
2. Why validate on both client and server?
3. What is the primary purpose of Input Sanitization?
4. What is a common mistake when implementing Input Sanitization?
Flashcards
Question
What is input sanitization?
Click to reveal answer
Answer
Removing or encoding dangerous content from user input to prevent attacks.
Question
What is whitelist validation?
Click to reveal answer
Answer
Only allowing known-good values instead of trying to block bad ones.
Question
What is HTML entity encoding?
Click to reveal answer
Answer
Converting <, >, &, ", ' to their HTML entity equivalents.
Question
Why use DOMPurify?
Click to reveal answer
Answer
To safely sanitize HTML content and prevent XSS attacks.
Question
What is Input Sanitization?
Click to reveal answer
Answer
Input Sanitization is a key concept in frontend development.
Revision Notes
Key Takeaways
- 1.Always validate on both client and server
- 2.Use whitelist validation when possible
- 3.Sanitize HTML with DOMPurify
- 4.Encode output based on context
- 5.Don't trust user input
Interview Tips
- •Explain client vs server validation
- •Discuss output encoding techniques
- •Know sanitization libraries and when to use them
Cheat Sheet
Input Sanitization Cheat Sheet
Validation
- Client-side: UX, can be bypassed
- Server-side: Security, mandatory
- Whitelist: Allow known-good values
Encoding
- HTML: escapeHtml()
- URL: encodeURIComponent()
- JavaScript: escapeJs()
- CSS: escapeCss()
Libraries
- DOMPurify: HTML sanitization
- validator.js: Input validation
- sanitize-html: HTML sanitization
React
- Auto-escapes by default
- Avoid dangerouslySetInnerHTML
- Use DOMPurify if needed
Best Practices
- Validate both sides
- Use whitelists
- Sanitize HTML output
- Test with malicious input