Skip to main content
In this lesson we cover input validation — the checks and protections applied to any data that users (or automated clients) supply to your application. Robust input validation reduces the attack surface for SQL/NoSQL injection, command injection, XSS, path traversal, XML External Entity (XXE) attacks, and other common vectors. Below we outline what to look for in an audit, demonstrate an automated audit run, and provide practical mitigations and patterns you can apply immediately.
Treat all external inputs as untrusted. Validate types, lengths, allowed values, and apply context-appropriate encoding before using data in queries, file operations, or rendered HTML.

Why input validation matters

Inputs arrive via HTML forms, APIs, file uploads, headers, query strings, and other external interfaces. Even if no human attacker is targeting your app, automated scanners and bots continuously probe services for weaknesses. The classic “Little Bobby Tables” example shows how untrusted input interpolated into a SQL statement can cause data loss (SQL injection). Any time you build queries, shell commands, file paths, or HTML using external data, assume the input could be malicious and take steps to validate, sanitize, or parameterize it. For example, avoid building SQL like:
SELECT * FROM users WHERE username = '...user input...' ;
An attacker-controlled username like:
'; DROP TABLE students; --
could terminate and append destructive SQL. Use parameterized queries and strong validation instead.

What to check for

Checklist of common input validation and related issues:
  • SQL injection: concatenation of raw inputs into SQL, unsanitized dynamic queries, unsafe stored procedure use.
  • NoSQL injection (MongoDB): unvalidated operator expressions (e.g., $where) or executing JavaScript in queries.
  • Command injection: passing unsanitized input to shell commands or child processes.
  • Cross-site scripting (XSS): missing output encoding or HTML sanitization for user content.
  • XXE (XML External Entity): insecure XML parsing that allows external entity resolution.
  • Path traversal: insufficient normalization/validation of file paths (../ attacks).
  • Request validation gaps: missing body-size limits, parameter pollution, lack of typed validation, or missing required fields.
Create a validation matrix mapping every endpoint to the protections applied (parameterized queries, schema validation, file handling constraints, rate limits, body-size limits, etc.). This helps prioritize fixes and ensures consistent coverage across routes.
A screenshot of a code editor (VS Code) with a file tree on the left and an open Markdown file titled "Input Validation Security Audit Report" listing security issues like SQL/NoSQL injection, command injection, XSS, XXE, path traversal, and request validation. The editor uses a dark blue theme.

Running an automated audit (example session)

Here is an example interactive session with an auditing assistant that generates a comprehensive input validation audit and writes it to the repository. Example CLI startup:
 jeremy@MACSTUDIO Express-login-demo % claude

* Welcome to Claude Code!

/help for help, /status for your current setup

cwd: /Users/jeremy/Repos/Claude Code Course/Express-login-demo

> Try "fix typecheck errors"

? for shortcuts
The assistant produces a detailed Markdown audit at audits/INPUT_VALIDATION_SECURITY_REPORT.md. Typical contents include a risk score, critical findings, proof-of-concept examples (for owned systems only), and targeted remediation steps.

Example audit summary

(The following is a consolidated, edited excerpt from a generated audit report.)
**Application:** Express Login Demo  
**Audit Date:** 2025-08-20  
**Auditor:** Claude Code  
**Overall Risk Score:** 6/10 (Medium-High)

## Executive Summary
This security audit analyzed the Express.js login application for input validation vulnerabilities across all endpoints and data processing functions. The application demonstrates good foundational security practices with parameterized queries and [express-validator](https://express-validator.github.io/docs/) usage, but has critical gaps in security controls that create notable risk.

**Key Findings**
- ✅ SQL Injection protection implemented correctly (parameterized queries)
- ✅ Input validation present ([express-validator](https://express-validator.github.io/docs/))
- ❌ Missing rate limiting on authentication endpoints — critical
- ❌ No request size limits — DoS through memory exhaustion possible
- ❌ Weak JWT secret configuration in production
- ❌ Incomplete input sanitization for some outputs
- ❌ Missing security headers in responses

Critical findings and remediation

1) Missing rate limiting (Critical)

  • CWE: CWE-307 (Improper Restriction of Excessive Authentication Attempts)
  • Risk: Without throttling on endpoints like /api/auth/login, attackers can brute-force credentials or overload the endpoint.
Exploitability (example brute-force loop — do not run against third-party systems):
# Brute force attack example (do not run against third-party systems)
for i in {1..1000}; do
  curl -s -X POST http://localhost:3000/api/auth/login \
    -H "Content-Type: application/json" \
    -d "{\"email\":\"test@example.com\",\"password\":\"attempt${i}\"}"
done
Remediation: use express-rate-limit to throttle attempts on login routes:
// server.js (or where you configure middlewares)
const express = require('express');
const rateLimit = require('express-rate-limit');

const app = express();

const loginLimiter = rateLimit({
  windowMs: 15 * 60 * 1000, // 15 minutes
  max: 5, // limit to 5 attempts per windowMs
  message: { error: 'Too many login attempts, please try again later.' },
  standardHeaders: true,
  legacyHeaders: false,
});

app.use('/api/auth/login', loginLimiter);

2) No request size limits (High)

  • Risk: Large request bodies can cause memory exhaustion and DoS.
Remediation: limit JSON and URL-encoded bodies:
// server.js
app.use(express.json({ limit: '100kb' })); // tune to your app's needs
app.use(express.urlencoded({ extended: true, limit: '100kb' }));

3) Weak JWT secret (High)

  • Risk: Weak or leaked JWT secrets allow attackers to forge tokens and impersonate users.
Remediation: store a strong secret in environment variables and rotate periodically. Example token verification middleware:
// middleware/auth.js
const jwt = require('jsonwebtoken');

const authenticateToken = (req, res, next) => {
  const authHeader = req.headers['authorization'] || '';
  const token = authHeader.startsWith('Bearer ') ? authHeader.slice(7) : null;
  if (!token) return res.status(401).json({ error: 'No token provided' });

  jwt.verify(token, process.env.JWT_SECRET, (err, payload) => {
    if (err) return res.status(403).json({ error: 'Invalid token' });
    req.user = payload; // attach decoded payload to request
    next();
  });
};

module.exports = authenticateToken;
Apply the middleware on protected routes:
const authenticateToken = require('./middleware/auth');
app.use('/api/protected', authenticateToken, protectedRouter);

4) SQL/NoSQL injection (Mitigations present)

  • Observation: Parameterized queries are used in many places, reducing SQL injection risk.
  • Remediation: Ensure all DB access uses parameterized statements or safe driver APIs. For MongoDB, validate query parameters and avoid allowing clients to inject operators like $where, $gt, or other expressions.

5) XSS and output encoding (Medium)

  • Observation: Some responses include user-supplied data without consistent HTML encoding.
  • Remediation: Apply context-aware output encoding (HTML, JavaScript, URL). Use templating engines with auto-escaping or sanitize HTML with libraries like DOMPurify.

6) Path traversal and file handling (Medium)

  • Observation: File access must validate and normalize paths to prevent ”../” escapes.
  • Remediation: Use path.join with a strict base directory, reject absolute paths and suspicious filenames, and enforce whitelist validation.

7) Request validation and schema checks (Medium)

  • Observation: express-validator is present but inconsistently applied.
  • Remediation: Define strict validation schemas for each endpoint (required fields, types, min/max lengths, allowed enumerations). Consider using a JSON schema validator like Ajv for consistent server-wide checks.

Prioritized remediation checklist

  • Implement rate limiting on auth endpoints (Immediate)
  • Add request body size limits (Immediate)
  • Replace weak JWT secret with secure env secret and rotate (High)
  • Ensure DB queries use parameterized methods (High)
  • Harden file endpoints against path traversal (High)
  • Apply consistent input validation and output encoding (Medium)
  • Add security headers (Helmet) and CSP as appropriate (Medium)

<Callout icon="warning" color="#FF6B6B">
Never run exploit or brute-force scripts against systems you do not own or have explicit permission to test. Use these techniques only in controlled environments.
</Callout>

## Quick defensive patterns and examples

Below are concise, actionable patterns to harden input handling in Node/Express apps.

- Parameterized SQL queries: always use your driver’s parameterization features (e.g., node-postgres) rather than string concatenation.
- Centralized input validation: use express-validator or Ajv to enforce schemas consistently across all endpoints.
- Rate limiting and body-size limits: throttling plus reasonable payload size caps mitigate credential stuffing and DoS risks.
- Output encoding and sanitization: escape or sanitize output according to the consumption context (HTML, JS, URL).
- Security headers: use Helmet to add common headers and reduce client-side attack surface.

Example: express-validator usage:
```javascript
// Example using express-validator
const { body, validationResult } = require('express-validator');

app.post('/api/auth/register', [
  body('email').isEmail().normalizeEmail(),
  body('password').isLength({ min: 8 }),
], (req, res) => {
  const errors = validationResult(req);
  if (!errors.isEmpty()) return res.status(400).json({ errors: errors.array() });
  // proceed with validated input
});
```text

Use Helmet for security headers:
```javascript
const helmet = require('helmet');
app.use(helmet());

Example validation matrix

Use a simple table to document which endpoints have which protections. This helps auditors and developers quickly spot gaps.
EndpointParameterized QueriesSchema ValidationFile HandlingRate LimitBody Size Limit
/api/auth/loginYesYes (partial)N/ANo (add)No (add)
/api/auth/registerYesYesN/ANo (recommend)Yes
/api/uploadN/AFile name validationRestricted to /uploadsYesYes
/api/users/:idYesID type checkingN/ANoYes
Adjust this matrix to your application and expand fields (e.g., output encoding, CSP, CSP nonce usage) as needed.

Example remediation workflow with an automated assistant

  1. Run the assistant to regenerate INPUT_VALIDATION_SECURITY_REPORT.md.
  2. Triage the report: prioritize rate limiting, size limits, secrets, and high-severity injection issues.
  3. Implement small, low-risk fixes first (rate limiting, body size, JWT secret handling).
  4. Re-run static checks and tests; iterate on validation schemas and automated tests for edge cases.
  5. Harden remaining areas: output encoding, file handling, and security headers.

Next topic

Input validation overlaps heavily with database security — validating and sanitizing data before it reaches the DB prevents many injection and corruption scenarios. Next, consider hardening queries, migration tooling, and connection handling. Additional remediation templates and resources are available in the course repository: https://github.com/JeremyMorgan/Claude-Code-Reviewing-Prompts

Watch Video