Skip to content

bypass-framework-safe-default-output-encoding

Ensure framework default output encoding

Frontend frameworks and libraries often protect against XSS by automatically encoding output so that any dangerous input is displayed as encoded text instead of executed as code. Bypassing these protections can allow attackers to inject malicious code. While there are a few legitimate cases for bypassing these controls, additional sanization or allow lists should always be added.

Examples

Insecure Example

function DangerousWrapperComponent(someHtml) {
    return <div dangerouslySetInnerHTML={someHtml} />;
}

function DangerousMarkdownComponent(markdown) {
    return <ReactMarkdown astPlugins={[parseHtml]} allowDangerousHtml children={markdown} />;
}
let html = this.sanitizer.bypassSecurityTrustHtml(value);

// passing SecurityContext.NONE removes the sanitizing, essentially making this a noop
let unsanitized = this.sanitizer.sanitize(SecurityContext.NONE, value);
# Unsafe because of the absence of autoescape=True
# Jinja2 DOES NOT autoescape by default.
jinja2.Environment(...)

Flask does not automatically escape Jinja templates unless they have .html, .htm, .xml, or .xhtml extensions. This could lead to XSS attacks. Use .html, .htm, .xml, or .xhtml for your template extensions. See https://flask.palletsprojects.com/en/1.1.x/templating/#jinja-setup for more information.

from flask import Flask, render_template
app = Flask(__name__)

@app.route("/index")
def index():
    page = request.args.get("page") # Arbitrary user input
    # Because the template filename is not ending with one of (.html, .htm, .xml, or .xhtml) it will NOT be escaped!
    return render_template("index.jinja2", page=page)

Secure Example

function SafeMarkdownComponent(markdown) {
    return <ReactMarkdown renderers={renderers} children={markdown} />;
}
// passing SecurityContext.HTML will properly encode for
let sanitized = this.sanitizer.sanitize(SecurityContext.HTML, value);
# Jinja2 would not autoescape, unless specified, like here.
jinja2.Environment(autoescape=True)
from flask import Flask, render_template
app = Flask(__name__)

@app.route("/index")
def index():
    page = request.args.get("page") # Arbitrary user input
    # Because the template filename with *.html it WILL be correctly escaped!
    return render_template("index.html", page=page)