Escaping

HTML escaping and safe content handling

2 min read 409 words

Kida auto-escapes output by default to prevent XSS vulnerabilities.

Autoescape

Withautoescape=True(default), output is HTML-escaped:

env = Environment(autoescape=True)
template = env.from_string("{{ content }}")
html = template.render(content="<script>alert('xss')</script>")
# Output: &lt;script&gt;alert('xss')&lt;/script&gt;

Special characters are replaced:

Character Escaped
< &lt;
> &gt;
& &amp;
" &quot;
' &#x27;

Disable Autoescape

For specific templates:

# Callable autoescape
def should_escape(template_name):
    if template_name is None:
        return True
    return template_name.endswith(".html")

env = Environment(autoescape=should_escape)

Globally (not recommended):

env = Environment(autoescape=False)

Safe Filter

Mark content as trusted HTML:

{{ html_content | safe }}

With optional reason for code review:

{{ cms_block | safe(reason="sanitized by bleach library") }}
{{ admin_html | safe(reason="admin-only content") }}

Markup Class

Create safe HTML in Python:

from kida import Markup

# String marked as safe
safe_html = Markup("<b>Bold</b>")
template.render(content=safe_html)  # Not escaped

Markup Operations

# Concatenation escapes unsafe strings
safe = Markup("<b>")
result = safe + "<script>"  # <b>&lt;script&gt;
result = safe + Markup("<i>")  # <b><i>

# Format escapes arguments
Markup("<p>{}</p>").format("<script>")
# <p>&lt;script&gt;</p>

Escape Function

from kida import Markup

# Escape a string
escaped = Markup.escape("<script>")
# &lt;script&gt;

Common Patterns

Pre-Sanitized Content

When content is already sanitized:

import bleach

cleaned = bleach.clean(user_html, tags=["b", "i", "a"])
template.render(content=Markup(cleaned))

Rendered Markdown

import markdown

html = markdown.markdown(source)
template.render(content=Markup(html))

HTML in JSON

Thetojsonfilter escapes for JavaScript:

<script>
const data = {{ user_data | tojson }};
</script>

Security Best Practices

Always Use Autoescape

# ✅ Autoescape on (default)
env = Environment(autoescape=True)

# ❌ Never disable globally
env = Environment(autoescape=False)

AuditsafeUsage

{# ✅ Document why it's safe #}
{{ content | safe(reason="sanitized by bleach") }}

{# ❌ Unmarked safe usage #}
{{ user_input | safe }}

Validate Content

# ✅ Sanitize before marking safe
cleaned = bleach.clean(content, tags=ALLOWED_TAGS)
Markup(cleaned)

# ❌ Never mark user input safe directly
Markup(request.form["content"])  # XSS vulnerability!

Escape Filter

Explicitly escape content:

{{ content | escape }}
{{ content | e }}  {# Short alias #}

Useful when autoescape is disabled for a template.

See Also