Safeguard
Application Security•

How do you prevent cross-site scripting in Rust web apps?

Rust's memory safety does not stop XSS. Use auto-escaping templates, avoid raw output wrappers, encode by context, and add a CSP as a second layer.

Safeguard Security Team
4 min read

Rust prevents memory bugs, not cross-site scripting. XSS is a logic flaw about where untrusted text lands in a page. Prevent it in Rust by rendering through auto-escaping template engines, treating every raw-output escape hatch as a review trigger, encoding for the context, and adding a content security policy as a second layer.

Why does a memory-safe language still have XSS?

XSS happens when a browser reads data as markup or script. The Rust compiler has no view of that. A handler that formats a request value into a String and returns it with an HTML content type is just as injectable as the same code in any language.

A constructed example: a search page builds its response with format! and drops the query string into a heading. A request whose query contains script tags returns a page that runs them. Nothing in the type system objected, because both values are strings.

What do the common template engines do by default?

Use an engine that escapes for you. The docs for the main ones say:

  • Askama's own example shows auto-escaping at work, and notes that a developer can disable it with the safe filter, which is why that filter deserves scrutiny.
  • Tera escapes by default for template files ending in .html, .htm and .xml. A template named report.txt or page.tpl is not escaped unless you call autoescape_on to change the suffix list.
  • Maud, a macro-based engine, offers a PreEscaped wrapper that renders the inner value without escaping.

So the practical rule is to grep for three things: the safe filter in templates, a PreEscaped wrapper in code, and any autoescape_on call that shrinks the default list. Each one is a place where a reviewer should ask where the value came from.

What about contexts that HTML escaping does not cover?

The OWASP XSS Prevention Cheat Sheet is clear that encoding depends on context. HTML entity encoding fits an element body. An attribute value must be quoted. A value inside JavaScript belongs only in a quoted string using a script-aware encoder. A URL parameter needs percent-encoding, and a full URL placed in an href needs its scheme checked, because a javascript scheme is not neutralised by entity encoding.

OWASP also lists places where untrusted data should not go at all: directly inside script or style blocks, in attribute or tag names, and inside whole event handler values. If a template puts a variable in one of those, change the design rather than the escape.

Where does user-supplied HTML fit?

Sometimes you must render user markup, such as a Markdown comment. Convert it, then pass the result through an HTML sanitiser with an allow list before you mark it safe. Mark it safe only at that last step, and keep the sanitiser output unchanged afterward. OWASP makes the same point for the browser side, where innerHTML should receive only sanitised output.

Does a content security policy help?

Yes, as defence in depth. MDN describes a CSP that allows only scripts carrying the right nonce or hash, blocks inline event handlers and javascript: URLs, and disables eval. MDN also states that a CSP is not an alternative to sanitising input. Roll one out in report-only mode first, so violations are logged without breaking pages.

In a Rust service, set the header in middleware, generate a fresh nonce per response, and pass it into the template so legitimate inline scripts carry it.

What to do

  1. Pick one template engine with auto-escaping on, and ban hand-built HTML strings in handlers.
  2. Add a CI search for the safe filter, PreEscaped, and autoescape_on. Require a comment explaining each hit.
  3. Check template file extensions against your engine's escaping rules.
  4. Never place untrusted data inside script blocks, style blocks or event handler attributes.
  5. Sanitise user HTML with an allow list before marking it safe.
  6. Ship a nonce-based CSP in report-only mode, then enforce it.
  7. Scan your crate dependencies too. Safeguard's free scan at gold.safeguard.sh/scan checks up to 100 dependencies in the browser.

Quick check

Does Tera escape a template named page.tpl?

Not by default. The Tera docs say autoescaping applies to .html, .htm and .xml files unless you change the list with autoescape_on.

Is a CSP a replacement for escaping?

No. MDN says setting a CSP is not an alternative to sanitising input. Treat it as a second layer.

Sources

Last reviewed: 18 October 2026.

Never miss an update

Weekly insights on software supply chain security, delivered to your inbox.

Self-healing security runs on Safeguard.

Your first fix PR is minutes away.

No sales call required, even your agent can complete the purchase over MCP.