RO EN

Applied Security for Web Forms (1/4): What the Attacker Wants and Form Design

Applied Security for Web Forms (1/4): What the Attacker Wants and Form Design ✨ Imagine generată cu AI
Doru Bulubașa
09 November 2026
5 views

In the article about the attack on our contact form, we told how the site became, for one day, someone else's phishing infrastructure: 109 emails sent to victims from our domain, from 41 IPs rented in the cloud. The most uncomfortable part was not the attack itself, but the defense report: the honeypot caught 2 messages out of 167, the minimum completion time caught 50 until the bot learned to wait, and the limit of three messages per hour per IP did not matter at all against 41 rotating addresses.

The wrong conclusion would be that these measures are useless. The correct conclusion is different: each measure stops only one type of attacker, and a public form needs multiple layers, each for a different type. This series is the practical part after the postmortem: what an attacker uses a form for, what defense layers exist, how each looks in ASP.NET Core, and a checklist you can apply to any form, from contact and newsletter to password reset.

Applied Security for Web Forms series:

What your form is useful for an attacker

The first step is not to choose a CAPTCHA, but to understand what someone can get from the form. A public form is an endpoint that accepts data from anyone and triggers effects: sends emails, writes to the database, displays content in an admin panel, sometimes costs money. Each effect is a target.

Abuse What the attacker gains What it costs you
Email relay (confirmations sent to the entered address) Phishing sent from your domain, with valid SPF and DKIM Domain reputation, restricted email account
Spam and SEO links to you Backlinks, offers, fraud attempts Time, noise, real messages buried
Injection into the email received by the team Links or HTML controlled by them in your inbox Internal, credible phishing because it comes from your own form
Stored XSS in the admin panel Code executed in the administrator's browser Stolen sessions, data access
Enumeration (login, reset, newsletter) List of addresses that have accounts with you Targets for credential stuffing and phishing
Costly endpoints (SMS, AI, upload) Revenue from fraud or simple sabotage Your bill

Note that only the first row was our incident. The others are just as real, they just make less noise.

Forms that cost money

An abused contact form damages your reputation. Other forms directly drain your account because each submission triggers a paid operation:

  • SMS codes (phone verification, two-step authentication). The fraud called SMS pumping or artificially inflated traffic uses your form to send thousands of SMS messages to premium-rate numbers, from which the attacker collects a share. Countermeasures: limits per destination number and country prefix, a whitelist of allowed countries, and a daily cost cap at the SMS provider.
  • AI endpoints (chatbots, content generation). Each request means paid tokens. You need quotas per user or per license, a cache for repeated questions, and a budget alert at the model provider.
  • File uploads. Storage, antivirus scanning, processing. Limit the file size and type before accepting it, not after.

General rule: for any public endpoint that triggers a paid operation, set a cost cap, not just a request limit.

Layers, not walls

Attackers are not all the same. A naive bot fills all fields instantly. A patient bot waits a few seconds. A serious bot rotates IPs from the cloud. And at the top of the ladder is a paid human, or a farm that solves CAPTCHAs in bulk for fractions of a cent. The table below shows why no single layer is enough:

Layer Naive bot Patient bot Rotating IPs Paid human
Design (nothing sent to visitor-controlled address) Eliminates gain Eliminates gain Eliminates gain Eliminates gain
Honeypot and minimum time Stops Partial Partial No
Challenge (Turnstile, reCAPTCHA, hCaptcha) Stops Stops Stops No
Rate limiting per IP Stops Stops No No
Global rate limiting per endpoint Caps Caps Caps Caps
Content scoring Depends on rules Depends on rules Depends on rules Partial

In this series, we go through eight layers: form design, validation and encoding, honeypot and minimum time signed on the server, challenge, rate limiting, content scoring, isolating email sending, and monitoring. The first row in the table is the only one that works against all, because it does not try to stop the attacker. It removes the reason to come. We start from there.

Layer 1: form design

The most important rule in the whole series: never send visitor-controlled content to a visitor-controlled address. A form that sends a confirmation to the entered address, with the entered name or message, is a free email sending service, signed with your domain. This is exactly what our attack exploited, and removing the confirmation was the decision with the greatest effect.

  • The confirmation is displayed on the page, not by email. The visitor immediately sees that the message arrived, and you no longer send anything outside.
  • If an email is absolutely necessary (newsletter, new account), use double opt-in, with a completely fixed text: no entered name, no message, only the confirmation link. An attacker can send at most a boring email that they do not control.
  • Identical responses for existing and non-existing. For password reset and newsletter, the message must be the same, for example "If the address exists, you will receive an email." Otherwise, the form becomes an enumeration tool.
  • What does not exist cannot be abused. Optional fields, attachments, and recipient selector are all attack surfaces. Keep only what you use.

A useful exercise before writing any code: take each form on the site and answer two questions. What effects does it trigger? And could any of them be used in someone else's name? If the answer to the second question is yes, change the design before adding protections.

What’s next

Design removes the reason for the most dangerous abuse, but the form still receives hostile data. In part two, we go into code: length limits and encoding, including in the email received by the team, a time token signed on the server with ASP.NET Core Data Protection, and server-side verification of a Cloudflare Turnstile challenge.