What are the types of injection attacks on web forms?
An HTML form does not hand you typed database values. The browser builds an entry list and encodes it; the endpoint receives strings, and sometimes files. Those strings stay data only while every downstream system treats them as data. When a SQL engine, an HTML parser, a mail server, a template compiler, a GraphQL parser, or a language model reads them as instructions, you have injection.
The dialects change. The failure mode does not. OWASP A05:2025 Injection defines it as untrusted input reaching an interpreter — a browser, a database, a command line — and being executed as a command. The Injection Prevention Cheat Sheet states the matching control: keep data separate from commands.
This is a defensive map for contact forms, signup forms, and the webhooks behind them. Examples stay schematic: a query assembled by concatenation, a dashboard that writes a message as markup, a subject copied into a header block. There are no copy-paste attack strings. For enctype, multipart filenames, and Origin, start with the lifecycle article and RFC 7578. We build Formgong. Product notes below match the public docs, and they do not abolish injection in systems you connect afterwards.
[ HTML form field : untrusted string or file ]
|
v
[ Form backend : field map + metadata ]
|
+--> SQL / document query keep it in a bound data slot
+--> HTML / notification encode for that context
+--> Mail or HTTP header structured field, no raw line breaks
+--> Template variable, never template source
+--> GraphQL typed variable, never concatenated text
+--> LLM or MCP tool untrusted content, limited toolsWhere injection sits in the 2025 rankings
OWASP Top 10:2025 moved Injection from third place (A03:2021) to fifth. The category did not shrink. The A05 write-up, checked 05.10.2026, says every application in the data set was tested for some form of injection, that 37 CWEs map into the category, and that it accounts for 62,445 CVEs. Cross-site scripting (CWE-79) is high frequency and lower average impact, with more than 30,000 CVEs. SQL injection (CWE-89) is lower frequency and higher impact, with more than 14,000 CVEs. Fifth place is a ranking change, not a reason to stop binding queries.
A05’s classical list is SQL, NoSQL, OS command, ORM, LDAP, and expression or OGNL injection. Prompt injection is pointed at LLM01:2025 and is not in A05’s CVE table, because the interpreter is a model. The matrix uses the stable CWE ids for the other rows.
Cloudflare’s 9 January 2024 API security report looked at WAF-managed rules on real API traffic. HTTP anomalies were the most common vector they described. Injection was second. Their point for form builders is that an API body becomes a database input the same way a form field or a query parameter does. A JSON fetch from an AI-built page is in that population. They treat a WAF as a backstop beside inventory and schema checks, not as a substitute for keeping data out of query text.
On 25 March 2024, CISA and the FBI asked software manufacturers to enforce prepared statements so user input stays data, and described sanitization as brittle. CWE-89’s observed-example list, which CWE presents as illustrations rather than a complete catalogue, still includes this defect inside newer wrappers: CVE-2024-6847, “SQL injection in an AI chatbot via a conversation message”, and CVE-2025-26794, “SQL injection in an email agent through a SQLite integration”. Those are CWE’s one-line descriptions. A chatbot transcript and an agent mail store are still strings someone concatenated into SQL.
Taxonomy matrix
Each row is one interpreter, how a form usually feeds it, the control that separates data from that language, and a citation checked on 05.10.2026. “Form-relevant” means a typical contact-form product touches that interpreter, or a do-it-yourself builder commonly adds it.
| Family | Interpreter | Form-relevant? | Primary defense | Key citation |
|---|---|---|---|---|
| SQL injection | SQL engine | Yes, if you store or search submissions yourself | Parameterized queries; allow-list identifiers | CWE-89, OWASP SQLi cheat sheet |
| NoSQL injection | Document query language | Yes, for JSON APIs that mirror fields into a document store | Keep values as scalars; reject operator objects | CWE-943, WSTG NoSQL |
| OS command injection | Shell | Situational; common in do-it-yourself mail and file hooks | Do not call a shell; pass an argument array | CWE-78, OWASP command defense |
| LDAP injection | Directory filter or DN | Situational; enterprise directory lookup and login | Encoder for that directory; allow-list attributes | CWE-90, OWASP LDAP cheat sheet |
| ORM injection | ORM query language or a raw-SQL escape hatch | Yes, if an inbox search is built by concatenation | Use the ORM’s bound parameters; allow-list sort columns | OWASP A05:2025, WSTG ORM |
| EL / OGNL injection | Expression language | Situational; Java templates and parsers that evaluate header text | Do not evaluate a field or a header as an expression | CWE-917, CVE-2017-5638 |
| XXE | XML parser | Situational; an uploaded XML file or a SOAP body | Disable DTDs, or disable external entities | CWE-611, OWASP XXE cheat sheet |
| XPath injection | XPath engine | Situational; a lookup that pastes a field into a path | Pass the value as an XPath variable | CWE-643, WSTG XPath |
| Code / eval injection | The language runtime | Situational; a rule or formula field passed to eval | Do not evaluate visitor text as code | CWE-94, CWE-95 |
| Cross-site scripting | HTML and JavaScript parsers | Yes; inbox UI, HTML email, thank-you pages | Encode for the output context | CWE-79, OWASP XSS cheat sheet |
| Server-side template injection | Template compiler | Yes, if customers edit notification templates | Pass fields as variables, never as template source | CWE-917, PortSwigger Research |
| CRLF / header injection | HTTP or mail header parser | Yes; Subject, Reply-To, custom headers | Reject raw line breaks; set headers in a structured API | CWE-93 |
| SMTP / IMAP injection | Mail protocol | Yes, for classic contact-form mailers | Structured mail API; separate headers from body | WSTG IMAP/SMTP |
| Path traversal | Filesystem path parser | Yes, when a file field is stored under a client name | Generated storage keys; drop directory parts | CWE-22, RFC 7578 |
| GraphQL injection and abuse | GraphQL parser and cost model | Situational; headless CMS mutations built from fields | Typed variables; depth and cost limits | OWASP GraphQL cheat sheet |
| LLM prompt injection | Language model | Yes, once you summarize, auto-reply, or classify submissions | Isolate untrusted text; least privilege; human approval | OWASP LLM01:2025 |
| MCP / tool-call abuse | Tool schema and the agent that trusts it | Yes, if an agent or MCP tool reads form text | Pin tool definitions; treat tool output as data | Invariant Labs, Microsoft |
The essays expand the same rows. Skip to what to fix first if you want the form-backend order only.
SQL injection
SQL injection is string-built query text. The application has a statement in mind — look up a row, insert a submission, filter an inbox — and pastes a form field into that text. The database parses the combined string, so a value from the HTTP body can become syntax. CWE-89 names the weakness. A05’s first scenario is this shape: a query assembled from a request parameter. The attack string printed next to that scenario is not repeated here.
Form products meet it in a search box, an “email contains” filter, a do-it-yourself insert that concatenates the field map, and a sort control that interpolates a column name. A hosted endpoint moves the insert off the customer’s server. The vendor’s storage, and any database a webhook consumer still builds by concatenation, remain interpreters.
The primary defense is a parameterized query: the SQL text is fixed, and each field is bound into a slot the engine does not parse as SQL. That is the first control on the OWASP SQL Injection Prevention Cheat Sheet and the control in the CISA and FBI alert. ORMs help when they generate those parameters. A05 warns that concatenation into the ORM’s query language, and stored procedures that build dynamic SQL internally, are still CWE-89. Quote-sanitizing is the brittle approach both documents set aside.
Identifiers are the exception. Table names, column names, and sort directions usually cannot be bound as data, so allow-list them in code. The role that inserts a contact row does not need other tenants or schema changes. Binding is the control. A WAF is only a backstop.
NoSQL injection
Document stores fail in a cousin of the SQL bug. The query is often JSON rather than a SQL string. If a form field is dropped into that document without a fixed type, a value that should be a string can arrive as an object of query operators. Some engines also evaluate a JavaScript expression supplied inside the query. CWE-943 names it. Testing is the WSTG NoSQL chapter; prevention is the NoSQL Security Cheat Sheet.
Urlencoded forms give you strings. A JSON body from an AI-built client can give you nested objects if you spread the body into a filter. Storing the submission as a document is fine. Using the request body as the filter is the bug.
On the server, decide that email is a string and that plan is an allow-listed token. Reject objects where you expected a scalar. Build the query in code. Leave any JavaScript evaluation clause disabled. “Not SQL” is not a defense.
OS command injection
Command injection is the shell as interpreter. A field appended to a command line is parsed for shell syntax. CWE-78 names it. A05’s third scenario is a lookup command whose argument is a request parameter. The attack string on that page is not repeated here.
A hosted endpoint rarely needs a shell to accept a POST. The family shows up around it: mail via a shell-out, a WordPress hook that builds a command from a field, an image tool that receives the upload name as a shell string, a webhook handler that passes the message to a system utility.
The OWASP command-injection defense cheat sheet prefers not invoking a shell. Use a library, or an argument array so the field stays one argument. Allow-list the program. Map a token such as “resize” to a fixed path in code. The worker that accepts forms should not be able to read host secrets because a field was unexpected.
LDAP injection
LDAP injection is the directory as interpreter. A login or lookup builds a search filter or a distinguished name by concatenating what the person typed, and filter or DN metacharacters change the search. CWE-90 names it. Keep the OWASP LDAP cheat sheet and the WSTG LDAP chapter (stable; the “latest” alias 404’d on 05.10.2026).
A public contact form rarely speaks LDAP. Enterprise cases do: an account-manager lookup, a support form that searches a directory, an intranet login that is still a form POST. A hosted endpoint does not encode LDAP for you. Do not ask a form vendor to become the directory client.
Escape values with the directory’s encoder — filter rules and DN rules differ — and allow-list attribute names. Prefer a library that takes a filter template plus parameters. Authenticate with the directory’s bind API, passing credentials as API arguments. The lookup account should not be able to read the whole tree.
Cross-site scripting
XSS is output injection. The field is stored as text, and a later page writes it into a document the browser parses as HTML or JavaScript: the inbox, an HTML notification, a thank-you screen, a webhook consumer’s admin UI. CWE-79 is the weakness, and A05 counts it inside Injection. For a form product this row is on by default, because you will render submissions.
The OWASP XSS Prevention Cheat Sheet is organized by output context. Encoding that is correct in HTML text is wrong in an attribute, a URL, or a script block. For an inbox, put the field in a text node or escape it for HTML text. A tiny allow-list sanitizer, applied on output, is the exception when you truly need a few tags.
Stored XSS is the visitor’s message running in the operator’s browser. Reflected XSS is the same request echoed on a thank-you or error page. DOM XSS is a client script assigning the field to a sink that parses HTML. The W3C Trusted Types Working Draft of 12 June 2024 lets a page require that values reaching those sinks come from a policy. Roth, Gröber, Baus, Krombholz, and Stock, Trust Me If You Can (USENIX Security 2024), found the browser control is real and the hard part is writing a correct sanitizer and deploying the policy, including around third parties. Trusted Types backstop DOM sinks. They do not replace encoding on the server that renders the inbox.
HTML email is the same bug aimed at the operator’s mail client. Keep the visitor’s words in a text part, or encode them as text inside HTML. The message does not get to supply the template.
Server-side template injection
Server-side template injection is user input compiled as template source. The field can then reach variables, objects the template language exposes, and sometimes the host. CWE-917 names it. PortSwigger Research published James Kettle’s Server-Side Template Injection in 2015, and the class has stayed in the testing guides. Notification features made it ordinary in form products. It did not need a new dialect in 2024.
Customers ask for custom email bodies, a chat template that includes the message, a CMS macro, an autoresponder edited in a dashboard. The safe version passes the submission as variables into a template the vendor wrote. The unsafe version concatenates the submission into the template string, or lets a customer template mark the message as raw markup.
Keep template source trusted. Visitors are not template authors. Autoescape variables. Sandbox the engine: no filesystem, no network, no arbitrary calls. A fixed layout with named slots is safer than a general-purpose language. A05 groups expression-language and OGNL injection with this row because the shape matches: a small language compiling a string that should have stayed data.
Header and CRLF injection
CWE-93 is a parser bug about line breaks. HTTP and mail headers are lines. A carriage return and line feed inside a value can start a new header or, after a blank line, a body. The field only has to contain the line endings the protocol uses as syntax.
Form products collect the fields that want to become headers: Subject, Reply-To copied from the visitor’s email, a display name, CC, a custom header a webhook asked for. The same concatenation instinct shows up in redirect URLs. The form lifecycle article’s filename warning is the file-shaped cousin.
Set headers through an API that takes a name and a value and rejects carriage return and line feed. Allow-list header names. Parse the visitor address and pass an address object, not a slice of header text. Dropping a subject that contains a line break is correct behaviour.
Email header and SMTP injection
Email injection is CRLF injection aimed at a mailer, plus SMTP and IMAP command grammar. The WSTG IMAP/SMTP chapter treats that conversation as an interpreter: a naive mailer emits extra commands or headers when fields are concatenated into the dialogue. Classic contact-form samples still show it. A few lines of PHP or a shell mailer, the visitor’s address in the headers, the message in the body.
Impact is extra recipients, a rewritten reply path, or a body that starts early. IMAP variants appear when a support tool builds a mailbox search from a field. If a hosted backend sends mail, its job is to refuse that concatenation. A webhook forwarded into a hand-rolled script puts the bug back.
Use a mail API that separates envelope recipients, headers, and body. Validate addresses with a parser. Keep the visitor’s message in the body as text. Set Reply-To from the validated address only. Rate-limit the form so a public key is not an open relay. Replacing a mailto: action only helps when the replacement is a structured sender.
Path traversal
Path traversal is a path parser as interpreter. If the server opens an upload under a base directory using the client filename, directory components in that name can escape the base. CWE-22 names it. Contact forms meet it when attachments are enabled. RFC 7578 tells receivers the filename is untrusted metadata: ignore directory components, and treat the bytes as untrusted content. How do HTML forms work? The HTTP request quotes those security considerations.
The mistakes are saving the multipart filename beside the application, using it as an object-storage key, passing it to a shell unpacker (also command injection), or echoing it in a download URL. Archives that contain their own paths are the same family one layer down.
Generate the storage key. Enforce type and size. Keep the download URL free of the client path. Store the original name only as a display string, encoded when shown. The control is ignoring the client’s path, not recognising a particular traversal spelling.
GraphQL injection and query abuse
GraphQL is another query language behind modern forms. Headless CMS forms and “submit via a mutation” starters place fields into a document the server parses. One layer is classic injection: concatenation lets a field change the document’s syntax. The other is a legal document that is ruinously deep, wide, or batched. The OWASP GraphQL Cheat Sheet covers injection prevention and denial-of-service controls (depth, amount, cost, timeouts, batching). The WSTG GraphQL chapter is the testing companion. Its “latest” alias redirected to the guide index on 05.10.2026; the stable URL is the live one.
A form endpoint should not expose a general GraphQL interpreter just to accept a name and a message. If the customer’s site posts a mutation, store a fixed document and pass fields as typed variables. Variables are data. Concatenated query text is an interpreter.
A public mutation that fans out to mail, chat, or a CRM is a business-logic cost that depth limits do not price. Rate-limit it, authenticate the form key, and keep the schema from reading unrelated objects. I did not add an academic survey beyond OWASP. The cheat sheet is enough, and no 2026 survey was required to state the defense.
LLM prompt injection
Prompt injection is the same failure mode with a new interpreter. A language model does not keep a firm boundary between instructions and data in the text it reads. If a submission is copied into the prompt, sentences in the message can be treated as instructions. OWASP LLM01:2025 defines it that way, and says RAG and fine-tuning do not fully remove it. The page treats fool-proof prevention as unclear, because of how generative models work. That is why this article does not claim any product makes prompt injection impossible.
Direct injection is the visitor typing text the model sees, such as a support form that drafts a reply. Indirect injection is the model reading a page, a file, a stored submission, or a tool result. Greshake and co-authors described the indirect case in Not what you’ve signed up for (arXiv:2302.12173). Liu and co-authors studied the same failure in integrated applications (arXiv:2306.05499, often cited as HouYi). A daily inbox summary is that shape: summaries, drafted replies, routing, and agents that call tools. LLM01 cites an email-assistant case, CVE-2024-5184. The mechanism is in the next article, Indirect Prompt Injection in MCP. Dated cases are in Indirect Prompt Injection Examples (2023–2026). The email path is in Indirect Prompt Injection via Email. The catalog case is in what tool poisoning in MCP is, with a scanner you can run. The host checks are on step-by-step prevention with a TypeScript policy layer.
LLM01’s mitigations reduce impact: constrain the model’s role, validate output in ordinary code, keep privileged functions in application code, require a person to approve high-risk actions, and mark untrusted content as data. A draft a human sends is a different product from a model that sends mail or edits rows. This is not a SQL parameter slot.
MCP and tool-call abuse
MCP tools are a second new interpreter in front of the same strings. A tool description tells the agent when to call the tool and how to fill arguments. A hostile description, or a tool result that contains instructions, can steer the agent after the operator approved what looked like a benign connector. Invariant Labs, on 1 April 2025, called this tool poisoning, including a rug pull: the server changes the description after approval. Microsoft’s 28 April 2025 note on indirect prompt injection in MCP makes the defender’s version of the same point. External content, including tool traffic, can carry instructions.
The form path is a tool that lists submissions, an agent that summarizes them, and a second tool that can send mail or write to a CRM. The submission or the tool description only has to persuade the model to call the second tool. That is indirect prompt injection plus a powerful action. This page does not include a poisoned tool document. Next in the series, Indirect Prompt Injection in MCP is that deep dive.
Show descriptions at approval time and pin a hash so a later change is visible. Separate tools that read forms from tools that send, delete, or spend. Treat tool results as untrusted content. Microsoft points at prompt shields, delimiters around untrusted text, and an allow-list of servers. Those layers reduce impact. They do not close it.
What are XXE, code injection, EL, ORM, and XPath?
OWASP’s common list and the A05 CWE catalogue already name these five. They were missing from the matrix above until 6 October 2026. Each note is a definition, a form-backend shape, and a mitigation. There is no attack string to copy.
XXE
CWE-611 is an XML parser that resolves an external entity in a document it should have treated as data. The parser can then read a local file, fetch a URL, or spend CPU expanding entities. OWASP’s XXE page and the XXE Prevention Cheat Sheet describe that outcome. A contact form meets it when a file input accepts an XML export, or when a SOAP or webhook body is parsed with external entities left on.
The cheat sheet’s primary control is to reject document-type declarations. If a DTD is actually required, disable external entities and external DTD loading, and keep XInclude off. Do not parse an upload with the parser’s defaults. Disabling DTDs does not, by itself, cover every XML resource limit. The cheat sheet points at the XML Security Cheat Sheet for expansion attacks.
Code and eval injection
CWE-94 is code injection: the program builds code from outside text and the runtime runs it. CWE-95 is the eval-shaped case, where that text reaches eval or a cousin. OWASP’s Code Injection page is the same family. A05’s CWE list includes both ids. A form meets it when a “custom rule”, a calculated field, or a webhook script passes the message to eval, new Function, or the host language’s equivalent.
Do not evaluate visitor text as code. Parse JSON with a JSON parser. Map an allow-listed token to a function you wrote. CWE-94 notes that Python’s ast.literal_eval still accepts deeply nested structures, so it is not a complete control for untrusted data. A sandbox only helps if it has no secrets, no filesystem, and no network.
EL and OGNL
Expression Language and OGNL injection is a small language compiling a string that should have stayed data. CWE-917 names expression-language injection. A05 lists “Expression Language (EL) or Object Graph Navigation Library (OGNL) injection” among the common types. A form meets it when a Java notification template interpolates a field into ${...}, or when a multipart parser evaluates header text.
CVE-2017-5638 is the public case people mean by that second shape. NVD’s description is narrower than the OGNL label: the Jakarta multipart parser in Apache Struts 2, 2.3.x before 2.3.32 and 2.5.x before 2.5.10.1, had incorrect exception handling during file upload, and a crafted Content-Type, Content-Disposition, or Content-Length header could run commands. NVD classifies the CVE as CWE-755 (exception handling). CERT VU#834067 calls the same issue OGNL in the Content-Type header and maps it to CWE-94. The form lesson is the parser, not a header to copy: do not evaluate a field or a multipart header as an expression, and run a parser version that no longer does.
ORM injection
ORM injection is SQL injection through the object layer. A05:2025 names it, and says unsanitized data inside ORM search parameters can return extra rows. The WSTG ORM chapter says the tester’s view is close to SQL injection, and that a safe ORM still breaks when a method accepts an unsanitized string. A form meets it in an inbox search that concatenates “email contains” into the ORM’s query language, or that calls a raw-SQL escape hatch with the field.
Use the ORM’s parameter binding. Allow-list sort columns, because identifiers usually cannot be bound. The SQL section above is the same control: concatenation into the query text is still injection, even when an ORM sits in front of it.
XPath injection
CWE-643 is an XPath expression built from outside text, so the visitor controls which nodes an XML store returns. The WSTG XPath chapter tests that case. A form meets it when a support lookup pastes the email field into an XPath predicate instead of passing it as a value.
Compile the expression with a variable slot, and bind the field into that slot. Do not concatenate the address into the path text. The injection-prevention cheat sheet groups XPath with SQL, LDAP, and OS commands: keep the query structure in your code.
What to fix first on a form backend
A typical form product does not have twelve equal emergencies. Rank by how often the interpreter is on the path, and by the blast radius when a field is misread. This order fits a product that accepts a browser POST, stores the field map, renders it, and fans out to email, chat, sheets, or webhooks.
- XSS in the inbox, HTML email, and thank-you page. Encode for the context. The victim is often the operator’s session.
- Email header injection. Subject and Reply-To are fields by design. Use a mail API that rejects raw line breaks.
- SQL or NoSQL, if you store or search submissions. Parameterize, and allow-list sort columns.
- Template injection, if users edit notification templates. Pass variables into a fixed template.
- Prompt injection and MCP abuse, if a model or tool reads the text. Isolate it, limit the tools, and require approval before a send or a delete.
- Command, LDAP, GraphQL, XXE, XPath, ORM raw queries, EL/OGNL, and eval, only when those interpreters are actually present. The five rows added on 6 October 2026 sit here unless the product parses XML, evaluates expressions, or builds those queries.
Then bind or encode for the interpreter in front of you, allow-list anything that becomes structure, and give each role one job. A WAF, as in Cloudflare’s 2024 report, is a backstop. Validation helps: a message box must allow punctuation, so OWASP treats validation as incomplete when those characters are special to the interpreter. Validate an email as an email and still encode it. Reject line breaks in a subject and still set the subject through an API.
What is new, and what is only newly popular
The classical grammars have not changed. SQL still parses query text, HTML still parses markup, SMTP still parses header lines. What changed by 2026 is where those interpreters run, and which new ones sit on the path.
Edge databases, serverless mail, GraphQL mutations, and AI builders that summarize leads are new places to concatenate into an old dialect. They are not a new SQL.
The model, and the tool schema in front of it, are the interpreters that are actually new. OWASP was right to put LLM01 on a separate list rather than pretend A05’s 62,445 CVEs already measure it. The Greshake and Liu papers are from 2023. The Invariant and Microsoft MCP notes are from April 2025. XXE, code injection, EL/OGNL, ORM, and XPath are older than that. They are in A05’s list and, as of 6 October 2026, in the matrix. On 05.10.2026 I did not find a 2026 injection family, distinct from these rows, that should jump this priority list. A new row would still have to name the interpreter and the control that keeps the field in a data slot.
Most matching incidents are still XSS in an admin UI, SQL or NoSQL in a do-it-yourself store, header injection in a mailer, or a public endpoint left open. The chatbot and email-agent lines on CWE-89 are old bugs in new wrappers. Ship the boring controls first.
What a hosted endpoint does, and what it leaves open
Formgong accepts a browser form POST, or a JSON POST from your page, and fans the field map out through structured channels: a web inbox, email, Telegram, Sheets, and signed webhooks. You point action at the endpoint and keep the static site. Setup is on HTML contact forms, the docs, and the HTML reference. The public field list is in llms.txt. Other backends are on the comparison hub.
You do not have to concatenate a field into SQL, a shell, or a raw header block. The lifecycle article records the wire contract, including EU storage in Cloudflare D1. Those are product facts, not a proof that every family is gone.
If you render submissions in your own HTML, you still encode them. If a webhook consumer concatenates a query, the SQL bug now lives there. If you paste the inbox into an unconstrained agent, or connect an MCP tool that can both read submissions and send mail, prompt injection is in scope again. Formgong does not make injection impossible, and it does not make prompt injection impossible. It keeps the browser POST out of glue you would otherwise write. The model you add is a new interpreter.
The Formspree and Web3Forms comparison is about which hosted product fits. This page is about which interpreter you still have to defend.
Next: prompt and tool injection
How do HTML forms work? The HTTP request established the field map as untrusted bytes. This article named the interpreters that map can reach, and the control for each one. Next in the series: Indirect Prompt Injection in MCP. It covers language models and MCP tools, when contact-form text is summarized, answered, or passed to a tool call.
The same rules apply there: schematic failure modes, primary sources, no copy-paste attacks, and no claim that a form backend abolishes prompt injection.
Encode the inbox, bind the queries, set mail headers through an API, and pass templates and GraphQL variables as data. If you add a model, limit what it may do with a submission, as the next article describes. Citations below were fetched or followed to a live URL on 05.10.2026. WSTG paths that 404’d were replaced with the stable pages in the table.
Frequently asked questions
What are the types of injection attacks on web forms?
A field is injected when a downstream interpreter parses it as instructions. On a form backend the types are SQL, NoSQL, ORM, OS command, LDAP, XSS, template injection, EL or OGNL, XXE, XPath, code or eval injection, header and SMTP injection, path traversal, GraphQL, and prompt or tool injection. OWASP A05:2025 defines the classical case. LLM01:2025 defines the model case.
Which injection family should a contact-form product fix first?
Encode submissions in the inbox and in HTML email, and set mail headers through an API that rejects raw line breaks. Parameterized queries come next if you store or search submissions. Prompt injection matters once a model reads the text, and the SQL control does not solve it.
Do parameterized queries also stop XSS and email header injection?
No. A bound SQL parameter keeps a field out of the SQL parser. It does nothing for the HTML parser that later renders the inbox, or for the mail parser that reads Subject and Reply-To. Each interpreter needs its own separation: context encoding for HTML, structured header fields for mail, variables for templates and GraphQL.
Is prompt injection the same bug as SQL injection?
It is the same kind of failure, data read as instructions, with a different interpreter. SQL has a parameter slot that OWASP and CISA treat as the primary fix. A language model does not. OWASP keeps prompt injection in LLM01:2025. Mitigations reduce impact. They do not promise the issue is gone.
Can a hosted form endpoint make injection impossible?
It can store the POST, send mail, and render an inbox with fields kept in data slots. A webhook consumer that still concatenates queries, and an agent that can act on submissions, are outside that boundary. Formgong is that kind of endpoint. It is not a guarantee against every interpreter you add later.
Sources and documentation
Official references for this guide: OWASP Top 10:2025 — A05 Injection, OWASP Top 10:2025, CWE-89: SQL Injection, CWE-79: Cross-site Scripting, CWE-93: CRLF Injection, CWE-78: OS Command Injection, CWE-22: Path Traversal, CWE-90: LDAP Injection, CWE-917: Template Engine Special Elements, CWE-611: XML External Entity Reference, CWE-755: Improper Handling of Exceptional Conditions, OWASP: XML External Entity Processing, CWE-94: Code Injection, CWE-95: Eval Injection, CWE-643: XPath Injection, OWASP XXE Prevention Cheat Sheet, OWASP: Code Injection, OWASP WSTG: Testing for XPath Injection, OWASP WSTG: Testing for ORM Injection, NVD: CVE-2017-5638, CERT VU#834067, CWE-943: NoSQL Injection, OWASP SQL Injection Prevention Cheat Sheet, OWASP XSS Prevention Cheat Sheet, OWASP LDAP Injection Prevention Cheat Sheet, OWASP Injection Prevention Cheat Sheet, OWASP OS Command Injection Defense Cheat Sheet, OWASP NoSQL Security Cheat Sheet, OWASP GraphQL Cheat Sheet, OWASP WSTG: Testing for NoSQL Injection, OWASP WSTG: Testing for LDAP Injection, OWASP WSTG: Testing for IMAP/SMTP Injection, OWASP WSTG: Testing GraphQL, PortSwigger Research: Server-Side Template Injection, Cloudflare: 2024 API Security and Management Report, CISA and FBI: Secure by Design Alert on SQL injection (25 March 2024), Roth et al., USENIX Security 2024: Trust Me If You Can, W3C Trusted Types Working Draft (12 June 2024), OWASP LLM01:2025 Prompt Injection, Greshake et al., arXiv:2302.12173, Liu et al., arXiv:2306.05499, Invariant Labs: MCP tool poisoning (1 April 2025), Microsoft: indirect prompt injection in MCP (28 April 2025), RFC 7578 — multipart/form-data. Formgong HTML integration, where data is stored.
Read this article as MarkdownRelated guides
- Lovable form submissions to email and Telegram
- HTML contact form without a backend: a working example
- How to send website form submissions to Telegram
- Contact form not sending email? Check where it stops
- Managing website leads in Telegram without a CRM
- Form backend for Lovable, Bolt, v0, Cursor
- Telegram bot for a contact form: build or skip?
- GDPR form backend: 7 checks before you choose
- Lovable form not sending email? 6 fixes
- Netlify Forms not working? React and Bolt fixes
- Stop contact form spam without a CAPTCHA
- GitHub Pages contact form: a working setup
- Mailto Form in HTML: Why It Fails and What to Use
- HTML form to Google Sheets: 2 free methods
- Webflow form submission limit: what to do at 50
- Turnstile vs reCAPTCHA vs hCaptcha for forms
- Contact Form 7 and Elementor forms to Telegram
- Squarespace contact form not sending email?
- Shopify contact form: where do messages go?
- Google Form to Telegram: free Apps Script way
- Wix contact form not sending email? Fixes
- EU / GDPR Formspree alternatives compared
- HTML form action attribute explained
- How do HTML forms work? The HTTP request
- Indirect Prompt Injection in MCP
- MCP Rug Pull Attack: Detect Tool Changes
- Form without a backend: 7 ways that work
- Thank-you page after form submission (HTML)
- Indirect Prompt Injection Examples (2023–2026)
- Indirect Prompt Injection via Email
- What Is Tool Poisoning in MCP?
- WordPress contact form without a plugin
- Send email from frontend JavaScript
- How to Prevent Indirect Prompt Injection
- Honeypot Form Field: How to Add One That Works
- Contact Form with File Upload (HTML, No PHP)
- Angular contact form without a backend
- Send form submissions to Slack or Discord without Zapier
- Verify a form webhook signature (HMAC-SHA256)
- Contact forms that send nothing: 793 AI-built sites tested
- v0 contact form that actually sends: 3 ways
- How we tested AI-built contact forms, and 12 bugs we hit
- Cloudflare vs Netlify free plan: hosting that never pauses
- EmailJS errors 400, 412 and 422: causes and fixes
- Resend errors in contact forms: domain, CORS, API key
- Supabase Edge Function blocked by CORS policy: 3 causes
- Formspree “Form not found” and other errors: fixes
- Web3Forms errors: “Invalid access key” and 403 explained