[Django]-Why and When to use Django mark_safe() function

36👍

✅

Django is a framework, which tries to do "the right" thing by default. This means when you do the most simple thing, you’re propably doing the right thing.

Now let’s look at some template in php and python:

PHP:

<? echo $foo ?>

May give:

<script src="evil">

Django:

{{ foo }}

Gives with the same input:

&gt;script src="evil"&lt;

Now assume, you want to place a link <a href="link">text</a>. Then django will render it as text using &lt;&gt; again. If you know what you’re doing, you can now use mark_safe to indicate that the text is trusted (i.e. not coming from userinput).

Usually you will use {{ foo|safe }} or {% autoescape off %}{{ foo }}{% endautoescape %} in your templates as django programmer, which is more clear when the string is declared as being safe.

So, where is mark_safe used? When you write own templatetags or filters, then you need to mark the string as safe from python, because the developer will assume, that {{ foo|mylinkifyfunction }} does the right thing, i.e., it escapes the url foo, but does not escape the HTML code <a href=""></a> around the url.

An example implementation could be:

def mylinkifyfunction(url):
    escaped_url = escape_url(url) # Take care of escaping the URL in a safe way
    generated_html = f'<a href="{escaped_url}">{escaped_url}</a>' # This string could be dangerous, if it would use url instead of escaped_url
    return mark_safe(generated_html) # mark the string as safe, because it should be rendered as unescaped HTML

The example assumes that escape_url returns a string that does not contain HTML or ".

18👍

It’s also worth noting that when building HTML code fragments it’s advised to use format_html(...) function instead of mark_safe and escaping all its arguments.

So, instead of writing:

mark_safe("%s <b>%s</b> %s" % (
    some_html,
    escape(some_text),
    escape(some_other_text),
))

You should instead use:

format_html("{} <b>{}</b> {}",
    mark_safe(some_html),
    some_text,
    some_other_text,
)

This has the advantage that you don’t need to apply escape() to each argument and risk a bug and an XSS vulnerability if you forget one.

Leave a comment