DEV Community

Cover image for HTML Emails Are Untrusted Input. Your Inbox UI Should Treat Them That Way.
Christopher H. Dutra
Christopher H. Dutra

Posted on AI-assisted

HTML Emails Are Untrusted Input. Your Inbox UI Should Treat Them That Way.

Here's something easy to forget when building a web inbox: an email is not a page that you created.

It is content supplied by somebody else, possibly somebody who would very much like your application to behave badly.

That means showing an HTML email inside your app deserves roughly the same suspicion as rendering user-submitted HTML anywhere else.

A pretty preview can be a security problem

Email HTML can contain images, links, inline styles, and deeply nested markup.

Depending on how it is handled, it can also contain active elements or URLs you never intended to allow in your application.

Even if your mail parser works perfectly, the next step—turning parsed HTML into a browser view—is a separate trust boundary.

The dangerous shortcut is taking arbitrary message HTML and inserting it into the main application DOM as though it were trusted content.

Build layers instead of betting on one filter

A safer pipeline looks like this:

Incoming MIME
    |
    v
Parse message parts
    |
    v
Sanitize HTML and limit sizes
    |
    v
Store permitted content
    |
    v
Sanitize again for display
    |
    v
Render in an isolated viewer
Enter fullscreen mode Exit fullscreen mode

Each step addresses a different concern.

Parsing turns the MIME envelope into understandable parts. Sanitizing removes dangerous markup and attributes. Size limits prevent absurdly large content from overwhelming the system.

An isolated viewer reduces the impact if something slips through.

This is defense in depth, not a guarantee that every email is safe.

Treat attributes and URLs as input, too

Stripping <script> tags alone doesn't solve the problem.

Event-handler attributes, unsupported embedded elements, and unsafe URL schemes also matter.

You also need to think about remote images.

An innocuous-looking email can load a remote resource when opened. That request may reveal information to the sender, even when no script runs.

A good viewer should make conscious decisions about remote content instead of treating every image URL as harmless.

Why plain text still matters

A developer can spend an hour perfecting an HTML verification template and forget its text alternative.

That's a mistake for accessibility, compatibility, and security-conscious readers.

A confirmation message should still explain what to do when styles or images are blocked.

For a signup email, I would check that the plain-text body contains the relevant explanation and a usable confirmation link or code.

A real implementation worth studying

10 Minute Temp Email has a receive-only web inbox.

Its published implementation notes describe server-side sanitization, a second browser-side pass with DOMPurify, and display inside a sandboxed iframe.

It also limits the attachment types that can be stored.

Those choices illustrate an important principle:

You don't have to trust the output of one layer just because that layer is yours.

The project goes into more detail in its email sanitization and deletion write-up.

Security isn't only about scripts

Even well-sanitized email can contain a convincing phishing message, a suspicious PDF, or a link to an attacker-controlled site.

The interface still needs clear sender information, understandable link behavior, and honest warnings where appropriate.

Security controls should make common mistakes less likely, not encourage people to click without thinking.

Final thought

The moment your application renders email, you've created a content-security feature whether you planned to or not.

Treat the message body as untrusted data, preserve the safe content users actually need, and isolate the reader from the rest of your application.

What has been your hardest HTML sanitization edge case?

Top comments (0)