Escaping mistakes in templates are a top cause of cross-site scripting vulnerabilities.
With autoescaping on, every {{ value }} is passed through HTML escaping: <, >, &, " and ' become entities. User input like <script> displays as text instead of running.
Escaping is tracked by type. A `Markup` string (from MarkupSafe) is considered already safe and is not escaped again. Ways to produce one:
{{ html|safe }}in a templateMarkup("<b>ok</b>")in Python- macros and
caller()returnMarkup, so they nest without double escaping
Use these only for HTML you produced or sanitized yourself. Markup has safe helpers: Markup("<a href='{}'>{}</a>").format(url, name) escapes the arguments and keeps the template part.
Where autoescaping is not enough:
- inside
<script>blocks, HTML escaping is the wrong encoding. Use{{ data|tojson }}, which produces JSON and escapes characters that could close the script tag. - in attributes, always quote:
href="{{ url }}". An unquoted attribute can be broken out of with a space. Escaping also doesn't stopjavascript:URLs; validate URLs. - in CSS and URLs, use dedicated encoders such as
|urlencode.
When autoescaping is off (text templates, or plain Environment()), {{ x|e }} escapes explicitly. You can switch it locally with {% autoescape true %}...{% endautoescape %}.
Never build HTML by concatenating user strings in Python and then marking the result safe; that defeats the whole system.
Example
from jinja2 import Environment
from markupsafe import Markup
env = Environment(autoescape=True)
t = env.from_string("""<p>{{ comment }}</p>
<p>{{ trusted }}</p>
<a href="{{ url }}">{{ link }}</a>
<script>const user = {{ user|tojson }};</script>""")
print(t.render(
comment="<img src=x onerror=alert(1)>",
trusted=Markup("<em>{}</em>").format("<b>escaped arg</b>"),
url='x" onclick="evil()', link=Markup("<b>Home</b>"),
user={"name": "</script><script>alert(1)"}))
# <p><img src=x onerror=alert(1)></p>
# <p><em><b>escaped arg</b></em></p>
# <a href="x" onclick="evil()"><b>Home</b></a>
# <script>const user = {"name": "\u003c/script\u003e\u003cscript\u003ealert(1)"};</script>Remember
- Autoescaping escapes every {{ }} unless the value is Markup
- Use |safe or Markup only for HTML you trust
- Use |tojson for data inside <script>; always quote attributes
Watch out for
- Applying |safe to user-provided rich text without sanitizing it first.