Contact forms look small because the browser page is small. The server behind one still accepts untrusted input, calls a bot-verification service, and sends a message into an operational system. That makes the form a trust boundary, not just a convenience feature.
The safest design narrows every decision. Accept the fields the business actually needs, validate them on the server, and keep the destination and sender policy under application control. OWASP recommends allowlisting and whole-value validation instead of treating a browser constraint as security.
What to accept
- Only named fields, with explicit maximum lengths and known formats.
- Only the industry and request types the business can route.
- Plain text that is normalized for logs and escaped before it is placed into HTML email.
- One bounded request body, with an explicit method and endpoint.
“Tell us anything” is a useful human prompt, not a validation rule. Give the message a limit. Reject control characters where they do not belong. Do not accept a recipient, a sender address, a redirect target, or a template name from the browser.
What to separate
Keep the form handler separate from the systems it does not need:
- Mail delivery. Use one fixed recipient and a controlled sender identity. The visitor may supply a reply-to address after it passes validation; they should not choose the transport headers.
- Verification. Check the bot challenge on the server, against the fixed site, action, and expected hostname. A token that merely exists in the request is not proof.
- Rate limiting. Rate-limit the form at the edge and in the handler. Keep the failure path cheap and fail closed when the limiter cannot make a decision.
- Browser policy. Allow the intended site origins and reject cross-site metadata that does not match them. CORS is not a replacement for authorization, but an exact origin policy reduces accidental reachability.
If a visitor can pick the destination, construct arbitrary headers, select a template, or trigger an internal URL, the form is doing much more than collecting an inquiry.
Controls that help
Use a layered control set: a narrow route and method, a small body limit, exact fields, server-side validation, a bot check, per-client rate limiting, same-origin checks, escaped output, a fixed recipient, and response headers that match the rest of the site.
Do not confuse a challenge with a denial-of-service control. OWASP notes that CAPTCHA-style puzzles can reduce form abuse, but they do not replace request limits and resource controls against an attacker who is trying to consume connections or processing capacity.
Document the data lifecycle too. If the handler sends mail and does not store a copy, say so. Define how long the mailbox and service provider retain the message, who can access it, and how an ordinary inquiry is deleted.
How to test it
- Send unknown fields, oversized values, invalid addresses, control characters, and an unsupported industry.
- Replay a valid-looking request without a bot token, with a token for the wrong hostname or action, and from a disallowed origin.
- Try duplicate headers, alternate methods, cross-site metadata, and a request body larger than the handler limit.
- Confirm that the delivered message contains escaped text, a fixed recipient, and no visitor-controlled transport headers.
- Compare form responses with the main origin: HSTS, framing, content type, referrer, and permissions policies should not drift without a reason.
How KernelHawk applies the lesson
KernelHawk reviews public forms as small applications. We trace input from the browser to validation, verification, rate limiting, delivery, logs, and retention. The goal is a bounded path a small team can understand and retest, not a long checklist that no one owns.
References
- OWASP, Input Validation Cheat Sheet, allowlisting, length limits, and server-side validation.
- OWASP, Denial of Service Cheat Sheet, rate limits, request limits, and the limits of CAPTCHA-style controls.
- OWASP, Cross-Site Request Forgery Prevention Cheat Sheet, request context and same-origin defenses.