Define the input contract

Validation begins with a declared shape: required fields, accepted types, length boundaries, enumerated values, and normalization rules. Browser attributes improve usability but do not enforce the server contract. Requests can arrive without the interface, with duplicate keys, or with unexpected nested arrays.

Normalize only when the transformation is unambiguous. Trimming surrounding whitespace is usually safe for a display name; changing case or removing punctuation may alter an identifier.

  • Reject unexpected structures.
  • Set explicit byte and item-count limits.
  • Use allowlists for enumerated values.
  • Keep raw input out of error pages and logs.

Keep validation and escaping separate

Validation decides whether data satisfies a business rule. Escaping prepares a value for a specific output context. HTML text, HTML attributes, URLs, JavaScript, SQL, and shell arguments require different handling; a value escaped for one is not automatically safe in another.

Use parameterized database queries regardless of prior validation. A valid surname can still contain an apostrophe, and a numeric-looking string can be represented in surprising ways.

Return errors without revealing the system

Associate a stable public error message with each field while logging a private diagnostic identifier. Avoid reflecting raw values into markup. For authentication and recovery flows, do not reveal whether an account exists unless the product explicitly requires that disclosure.

CSRF protection, rate limits, authorization, and file inspection are separate controls. A form can be perfectly validated and still perform an action the requester is not allowed to take.

  • Regenerate CSRF tokens according to the workflow.
  • Re-check authorization immediately before the write.
  • Use generic external errors and detailed internal logs.
  • Test arrays, duplicate fields, null bytes, and oversized bodies.
Verification checkpoint

Submit the form without JavaScript, replay it with missing and duplicated fields, and confirm that invalid requests do not create partial writes or expose raw values.