# Email headers/routing not falsified or misleading

- **Pattern:** `ab-001252` (`email-sms-compliance.content-delivery.no-misleading-headers`)
- **Severity:** low
- **Lifecycle:** active
- **Last modified:** 2026-04-18
- **Canonical URL:** https://auditbuffet.com/patterns/ab-001252
- **License:** CC-BY-4.0 — attribute to AuditBuffet Pattern Catalog (https://auditbuffet.com/patterns/ab-001252)

## 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.

```ts
// 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-from` which 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 like `Alice\r\nBcc: attacker@evil.com` injected into the `From` display name would add a Bcc header, redirecting copies of emails without the user's knowledge. (2) **Misleading routing**: `Reply-To` set to a domain completely different from `From` without disclosure, or `Return-Path`/`Sender` headers 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`, `\0` characters before inclusion)? (c) are `Reply-To`, `Sender`, or custom `X-*` 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-To` either matches `From` or 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\0` characters — this is a header injection vulnerability. Or: `Reply-To` is 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:

  ```ts
  // 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) — https://www.ftc.gov/business-guidance/resources/can-spam-act-compliance-guide-business
- cwe CWE-93
- owasp A03

Taxons: injection-and-input-trust, regulatory-conformance

HTML version: https://auditbuffet.com/patterns/ab-001252
