Email headers/routing not falsified or misleading
Why it matters
CAN-SPAM §5(a)(1) prohibits false or misleading header information. Header injection (CWE-93, OWASP A03 Injection) occurs when user-supplied values — a display name from a profile, a subject template, a reply-to address — are interpolated into email headers without stripping \r\n\0 characters. A malicious value like Alice\r\nBcc: attacker@evil.com added to the From display name injects a Bcc header, silently copying every email to an attacker-controlled address. This is both a CAN-SPAM violation (falsified headers) and a data exfiltration vector for transactional emails containing sensitive content (receipts, password resets, order confirmations).
Severity rationale
Low because exploiting header injection requires a user to control a profile field that reaches an email header, but the potential impact includes bulk email exfiltration and strict-liability CAN-SPAM violations for every affected message.
Remediation
Sanitize every user-supplied value before interpolating it into an email header field by stripping newlines, carriage returns, and null bytes.
// lib/email.ts — header sanitization
function sanitizeHeader(value: string): string {
return value.replace(/[\r\n\0]/g, ' ').trim().slice(0, 998) // RFC 2822 line limit
}
// Apply before every header value derived from user input
const fromName = sanitizeHeader(user.displayName)
headers['From'] = `${fromName} <${FROM_EMAIL}>`
For Reply-To, only set it when you have a legitimate fixed alternate address (e.g., a support alias on your domain). Never set Reply-To to a value derived from user input without sanitization, and do not set it to a completely different organization's domain.
Detection
-
ID:
no-misleading-headers -
Severity:
low -
What to look for: Enumerate every relevant item. CAN-SPAM Section 5(a)(1) prohibits false or misleading header information. This check — distinct from
accurate-fromwhich covers domain ownership — specifically examines two failure modes: (1) Header injection: user-supplied values (display name from user profile, subject preferences, reply-to address set by form input) that are interpolated directly into email headers without sanitization. A value likeAlice\r\nBcc: attacker@evil.cominjected into theFromdisplay name would add a Bcc header, redirecting copies of emails without the user's knowledge. (2) Misleading routing:Reply-Toset to a domain completely different fromFromwithout disclosure, orReturn-Path/Senderheaders that misrepresent the sending infrastructure. Look at every email-sending call: (a) are any header values derived from user-supplied fields (name, display name, subject template, reply address)? (b) if so, are those values sanitized (stripping\r,\n,\0characters before inclusion)? (c) areReply-To,Sender, or customX-*headers added that could mislead recipients about origin? -
Pass criteria: At least 1 of the following conditions is met. No user-supplied values are interpolated into email headers without sanitization (stripping newline characters).
Reply-Toeither matchesFromor is a legitimate fixed alternate address. No routing headers are manipulated to falsify origin. -
Fail criteria: A user-supplied value (display name from user profile, name from form submission, custom subject template field) is string-interpolated into a
From,Reply-To,Subject, or other header without stripping\r\n\0characters — this is a header injection vulnerability. Or:Reply-Tois set to a completely different organization's domain with no disclosure. -
Skip (N/A) when: The application sends no email.
-
Detail on fail: Specify the injection vector clearly. Example:
"sendEmail() accepts a displayName parameter and interpolates it directly into the From header as '\${displayName} <noreply@example.com>' with no sanitization. A newline in displayName would inject arbitrary headers."or"Reply-To header is set to a different company domain (replies@third-party.com) while From shows the application's domain — misleading routing without disclosure.". -
Remediation: Sanitize any user-supplied values before including them in headers:
// Sanitize user-supplied values before placing in email headers function sanitizeHeader(value: string): string { // Remove newlines, carriage returns, and null bytes — these are header injection vectors return value.replace(/[\r\n\0]/g, ' ').trim().slice(0, 998) // RFC 2822 header line limit } // Usage: const fromName = sanitizeHeader(user.displayName) headers['From'] = `${fromName} <${FROM_EMAIL}>`For
Reply-To, only set it if you have a specific legitimate need (e.g., a support alias). Do not set it to a different company's domain.
External references
- external · CAN-SPAM-§5(a)(1) — CAN-SPAM Act §5(a)(1) — False or misleading header information prohibited
- cwe · CWE-93 — Improper Neutralization of CRLF Sequences (Email Header Injection)
- owasp:2021 · A03 — Injection — email header injection via unsanitized user input
Taxons
History
- 2026-04-18·v1.0.0·Initial import from email-sms-compliance·automated