ASVS 5.0 V1.3.11 (Level 2) requires that user input is sanitized before it is passed to mail systems, to protect against SMTP and IMAP injection. In practice, this means rejecting carriage returns and line feeds in any value that becomes a header or protocol command.
Why it matters
A contact form that places a visitor's address in a From or Reply-To header can be abused: an address followed by \r\nBcc: victims@example.com adds recipients, and your server becomes a spam relay with your domain's reputation. The same line break trick can inject extra commands into raw SMTP or IMAP sessions.
How to implement it
Use a mail library with a structured message API, never raw header strings, and validate fields.
from email.message import EmailMessage
from email.utils import parseaddr
def build(to_addr: str, subject: str, body: str) -> EmailMessage:
for v in (to_addr, subject):
if "\r" in v or "\n" in v:
raise ValueError("line break in header value")
name, addr = parseaddr(to_addr)
if not addr or "@" not in addr:
raise ValueError("bad address")
msg = EmailMessage()
msg["To"] = addr
msg["Subject"] = subject
msg.set_content(body)
return msg
Modern libraries refuse line breaks in headers, but check yours. Do not let users set recipients freely. Send contact form mail to a fixed address and put the visitor's email only in the body or Reply-To after validation. For IMAP, quote search terms using the library's escaping and never build commands by concatenation.
How to test it
Submit a@example.com%0d%0aBcc:x@example.com and the same with a literal line break in the address, name and subject fields. Check the sent message headers in a test inbox: there must be no extra Bcc or header.
Common mistakes
- Validating the email format with a pattern that allows line breaks.
- Letting the subject or name carry a line break.
- Using the
mail()function of a language with raw header strings.
Quick check
Which characters enable header injection?
Carriage return and line feed. Reject both in any value that ends up in a header or command.