Disabled HTML escaping in Mustache

Disabled HTML escaping in Mustache

Description

Replacing a template engine's HTML escape function with one that returns input unchanged prevents automatic escaping of special characters. In Mustache.js, overriding Mustache.escape this way can let attacker-controlled content become executable HTML and cause cross-site scripting (XSS).

Potential impact

  • Injected scripts may run in a user's browser.
  • Data accessible to those scripts, including unprotected cookies or session information, may be stolen.
  • Attackers may alter the displayed page and undermine trust in the service.

Remediation

  • Keep the template's default escaping when inserting values into HTML text.
  • Do not replace Mustache.escape with a function that returns input unchanged.
  • Validate and encode values for their actual output context, such as URLs, JavaScript or CSS. HTML escaping does not protect every context.

Examples

Before

javascript
const Mustache = require('mustache');

// BAD: Override HTML escaping to return the input unchanged
Mustache.escape = function(text) {
  return text;
};

const unsafeTemplate = '<div>{{userInput}}</div>';
const html = Mustache.render(unsafeTemplate, { userInput: req.query.text });

After

javascript
const Mustache = require('mustache');

// GOOD: Keep the default Mustache escaping
const safeTemplate = '<div>{{userInput}}</div>';
const html = Mustache.render(safeTemplate, { userInput: req.query.text });

The first excerpt inserts unescaped input into the page. The second escapes special characters in this HTML text position. The excerpts assume separate executions: calling require again does not restore an already overridden global function.

References