Server Responsibilities · 3 of 6

Validation

Never trust input. Every field arriving from a client, partner API, or message queue is a potential attacker — until your server has parsed, type-checked, and constrained it.

SchemasZodPydanticJSON SchemaOpenAPISanitization
← Back to Server Side
Quick Facts

At a Glance

Basic Concepts

  • Validation = is this input shaped right? (types, ranges, required fields).
  • Sanitization = stripping or escaping content before it crosses a dangerous boundary (HTML, SQL, shell).
  • Trust boundary: any line your data crosses where the source can't be vouched for. Validate every time you cross one.
  • Parse, don't validate: turn raw input into a strongly-typed value object once — afterwards the type system carries the proof.
  • Fail fast: reject malformed input at the edge with a clear error, before it reaches business logic.
Layers

What to Validate, Where

LayerResponsibilityTools
Network edge / WAFShape attacks: huge bodies, malformed JSON, known bad payloads.Cloudflare, AWS WAF, ModSecurity.
API contractType, required-ness, enum values, format (UUID, email, ISO date).Zod, Pydantic, Joi, class-validator, FluentValidation, JSON Schema.
Domain / business rules"End date after start date," "discount ≤ subtotal," "user owns this resource."Hand-written invariants in the domain layer.
PersistenceNOT NULL, CHECK, UNIQUE, foreign keys — last line of defense.Database constraints.

Each layer catches what the layer above missed. Skipping DB constraints because "the app validates it" is how data corruption ships.

Schemas

Schema-First Validation

TS
Zod / Valibot / ArkType

Define a schema, infer the type. Parsing fails loudly with structured errors.

Python
Pydantic

The de-facto standard; powers FastAPI's request/response models.

.NET
FluentValidation / Data Annotations

Declarative rules, integrated with model binding.

Java
Bean Validation (Hibernate Validator)

JSR-380 — annotate fields with @NotNull, @Email, @Size.

Go
go-playground/validator

Struct tags drive validation rules.

Cross-language
JSON Schema / OpenAPI

Single source of truth — generate validators, clients, and docs from one spec.

Sanitization

Output Encoding by Context

Most "sanitization" bugs are actually output-encoding bugs. The same string is safe in one context and dangerous in another. Encode at the moment you cross the boundary, never earlier.

  • HTML → escape < > & " '. Use templating that escapes by default; use DOMPurify for rich-text input.
  • SQL → never concatenate. Use parameterized queries / prepared statements.
  • Shell → avoid altogether; if unavoidable, use execFile-style APIs that take an arg array, never exec with a string.
  • URLs → encodeURIComponent for path/query, allow-list scheme & host for redirects.
  • Filenames → normalize, strip .., anchor under a known root.
  • LDAP, XPath, NoSQL — each has its own injection class. Use the driver's parameterization.
Hard Cases

Tricky Inputs

Files & Uploads
  • Cap size at the proxy and the app — don't let a 10 GB upload OOM your worker.
  • Trust the file's bytes (magic numbers), not its declared MIME or extension.
  • Re-encode images server-side (ImageMagick, Sharp) — strips polyglots, EXIF, and decompression bombs.
  • Store outside the web root; serve via signed URLs from object storage.
  • Virus-scan untrusted files before downstream consumption.
Numbers & Money

Reject NaN, Infinity, and negatives where they don't make sense. Use decimal types (Postgres numeric, Java BigDecimal) for currency — never float. Pin currency code; a missing one defaults to nothing safe.

Dates, Times, Time Zones

Accept ISO-8601 only. Store UTC; convert at the edge. Reject dates outside a sensible window — a birthday in the year 3024 is almost certainly an attack or a bug.

JSON, XML, YAML Parsing
  • XXE: disable external entity resolution in every XML parser.
  • YAML: use safe_load / load(SafeConstructor) — default loaders can instantiate arbitrary types.
  • Prototype pollution: reject keys named __proto__, constructor, prototype in JS object merges.
  • Cap depth and total size before parsing — a deeply nested JSON can DoS the parser.
Mass Assignment

Don't bind a request body straight onto your DB model. The client may send { isAdmin: true }. Use an explicit allow-list — DTOs, "strong parameters," permit(), or schema .strip() / .strict().

Pitfalls

Common Mistakes

  • Validating only on the client. JS-side checks are UX; the server must re-validate.
  • Allow-list vs deny-list. Deny-listing dangerous characters always misses one. Allow-list what's permitted.
  • Returning attacker input verbatim in errors. A reflected XSS in a 400 page is still XSS.
  • Schemas drifting from runtime. If your OpenAPI spec and your handler disagree, the spec is fiction. Generate one from the other.
Continue

Other Server Responsibilities