← All posts
5 min read

Input Validation: Enforcing Expectations at Trusted Boundaries

#technology#security#input-validation#software
📑 On this page

Every interface receives data from somewhere else.

Browsers, mobile apps, files, APIs, queues, and databases can send malformed, stale, hostile, or simply unexpected values.

Validate external input at the boundary where the system exercises authority.

Client-side checks improve usability, but an attacker can bypass the client and call the server directly.

A concrete example: item quantity

The website prevents a shopper from entering a negative quantity.

An attacker sends an API request manually:

quantity: -100

The server validates the business rule and rejects it. Without server enforcement, the client interface would be mistaken for a security boundary.

What counts as input?

Input includes:

  • URL and query parameters,
  • headers,
  • cookies,
  • JSON and form bodies,
  • uploaded files,
  • database records,
  • queue messages,
  • environment variables,
  • command-line arguments,
  • and third-party API responses.

Trust depends on the boundary and authority, not whether the source is "internal."

Syntactic validation

Syntactic validation checks representation:

  • type,
  • format,
  • length,
  • encoding,
  • required fields,
  • and structure.

Examples:

  • integer,
  • valid date format,
  • UUID shape,
  • maximum 200 characters,
  • known JSON schema.

Semantic validation

Semantic validation checks meaning:

  • quantity must be positive,
  • end date follows start date,
  • account is active,
  • currency matches merchant,
  • requested transition is allowed.

A value can be syntactically valid and still violate business rules.

Allow lists

When accepted values are known, use an allow list:

status in {draft, submitted, approved}

Trying to list every dangerous string is brittle because attackers can encode or combine values in unexpected ways.

Allow the supported domain rather than guessing all invalid possibilities.

Type and coercion

Automatic conversion can create ambiguity:

  • empty string becomes zero,
  • "false" becomes true in some contexts,
  • large number loses precision,
  • date changes time zone.

Parse explicitly and reject ambiguous values. Use domain types for money, identifiers, and dates.

Length and size limits

Set limits before expensive processing:

  • body size,
  • file size,
  • string length,
  • array length,
  • nesting depth,
  • decompressed size,
  • and number of fields.

A tiny compressed input can expand enormously, creating a decompression bomb.

Numeric ranges

Check:

  • minimum,
  • maximum,
  • precision,
  • sign,
  • and overflow.

Financial values should use decimal or integer minor units according to the domain, not unsafe floating-point assumptions.

Dates and time

Validate:

  • format,
  • time zone,
  • impossible dates,
  • allowed range,
  • and business meaning.

"2026-02-30" is structured like a date but does not exist. A birth date in the future is a semantic error.

Identifiers

A valid identifier format does not prove access.

After parsing an order ID, the server must still verify:

  • resource exists,
  • caller owns or may access it,
  • and requested action is allowed.

Validation and authorization solve different problems.

File uploads

Validate:

  • total size,
  • actual content type,
  • extension where relevant,
  • parser behavior,
  • dimensions,
  • archive contents,
  • filename,
  • and storage location.

Do not trust the browser-provided MIME type or original filename alone.

Path handling

User-controlled paths can escape intended directories through values such as ../.

Use safe storage APIs, generated filenames, resolved-path checks, and explicit roots. Do not build filesystem paths through raw string concatenation.

Object identifiers are often safer than exposing server paths.

Canonicalization

The same logical value can have several representations through Unicode, URL encoding, case, path separators, or repeated decoding.

Normalize once using a defined standard before applying comparisons and allow lists, then preserve the original only when the product needs it. Validation before one layer decodes the value again can allow a forbidden representation to reappear.

Validation versus sanitization

Validation decides whether input is acceptable.

Sanitization transforms input into a permitted form, such as normalizing a phone number. Silent transformation can change meaning, so use it only with clear rules.

Do not "sanitize" SQL or HTML with homemade character removal.

Output encoding

Input validation does not replace context-aware output encoding.

A person's name may legitimately contain punctuation that becomes dangerous only when inserted into HTML, JavaScript, SQL, or a shell incorrectly.

Keep data separate from executable syntax using safe APIs and encode for the destination context.

Database queries

Use parameterized queries.

Even a validated value should not be concatenated into SQL. Validation can change later, and one missed field reintroduces injection.

Parameterized structure is the primary control; validation enforces domain rules.

Error messages

Return useful but safe errors:

  • field,
  • expected rule,
  • retry guidance.

Avoid exposing stack traces, SQL, secret values, or internal paths.

Collect detailed context securely for operators.

Validation location

Validate at each trust boundary:

  • edge for size and syntax,
  • application for domain rules,
  • database for constraints,
  • consumer for message contract.

Layers have different authority. Do not rely on one distant upstream check forever.

Schema validation

Schemas can enforce:

  • required fields,
  • types,
  • allowed values,
  • nesting,
  • and version.

Generated validators improve consistency, but custom business logic remains necessary.

Reject or deliberately ignore unknown fields according to compatibility and security needs.

Testing validation

Test:

  • boundaries,
  • missing versus null,
  • wrong type,
  • extra fields,
  • Unicode,
  • huge input,
  • malformed encoding,
  • duplicate fields,
  • and business-rule conflicts.

Property-based testing and fuzzing can discover unexpected parser behavior.

Knowledge check

  1. Why is client-side validation not a security boundary?
  2. How do syntactic and semantic validation differ?
  3. Why are allow lists usually stronger than block lists?
  4. Why does a valid resource ID not prove authorization?
  5. How does validation differ from output encoding?

The one idea to remember

Validate every external value for structure, type, size, range, and business meaning at the trusted server boundary. Keep validation separate from authorization and context-aware encoding, and use safe structured APIs instead of trying to remove dangerous characters.