Cross-site scripting through jQuery html()

Cross-site scripting through jQuery html()

Description

jQuery's html() interprets its input as HTML and inserts it into the DOM. Passing untrusted content directly to it can cause cross-site scripting (XSS): an attacker's markup may execute in another user's browser, enabling session abuse, impersonation or phishing.

Potential impact

  • Scripts may steal accessible session cookies or sensitive page data.
  • Malicious scripts may run in a user's browser.
  • The page may be altered to display deceptive forms or other phishing content.

Remediation

  • Use jQuery's text() instead of html() to display text.
  • If text must be inserted into an HTML body, escape it for that context. If actual markup must be allowed, restrict tags and attributes with a trusted HTML sanitizer.
  • Handle values for their output location. HTML encoding does not replace JavaScript handling or URL validation.

Examples

Before

javascript
// Input is interpreted as HTML
function showMessage(userInput) {
    $("#message").html(userInput);
}
// Input such as <script>alert('XSS')</script> can execute a script.

After

The following are separate alternatives. The first displays text directly; the second encodes the string before inserting it into an HTML body.

javascript
// Alternative 1: use text()
function showMessage(userInput) {
    $("#message").text(userInput);
}

// Alternative 2: escape for HTML
function escapeHtml(str) {
    return str.replace(/[&<>"']/g, function(tag) {
        const chars = {'&':'&amp;', '<':'&lt;', '>':'&gt;', '"':'&quot;', "'":'&#39;'};
        return chars[tag] || tag;
    });
}
function showMessage(userInput) {
    $("#message").html(escapeHtml(userInput));
}

Explanation:

  • Before: Passing input directly to html() allows supplied markup to be interpreted and scripts to execute.
  • After: text() displays the input as text. Alternatively, escaping the input before this HTML insertion prevents it from being interpreted as markup.

References