You never want to call html_safe in a Rails template

Sometimes when looking at old Rails code, especially code that’s existed since Rails2 days and been upgraded along with Rails, I sometimes see `html_safe` called directly in a view template:

WRONG!
<%= magicfy("foo").html_safe %>

It shows up in the old code because when someone (possibly me) upgraded to the version of Rails that first started tagging strings `html_safe`, there was some helper method producing HTML code that was winding up escaped and visible in the rendered output, and simply dropping in an `html_safe` seemed like a quick fix.

This is pretty much always wrong. In most cases, it’s going to be a potential security problem. In some cases it won’t be, but it can turn into one easily enough that it’s probably always a design flaw or ‘smell’. You’re fighting with Rails intended method of protecting you from HTML injection attacks — or just mal-formed HTML.

To illustrate, let’s imagine a helper method `magicfy` that simply wraps it’s argument in a span.magic:

def magicfy(value)
   content_tag(:span, value, :class => "magic")
end

Now let’s imagine we pass in an argument with some non-html-safe stuff in it.

<%= magicfy("1 > 2") %>

Because we’re using `content_tag`, the `>` will get properly escaped to a `&gt;` and the string returned will be marked html_safe already. content_tag takes care of it for us. We’ll get returned the equivalent of:

'<span class="magic"> 1 &gt; 2</span>'.html_safe

Same if the argument was a string that originally came from user-input in an attempt to do some HTML injection…

user_input = "<script> ..."
magicfy(user_input)
#=>  "<span class=\"magic\">&amp;lt;script&amp;gt; ...".html_safe

So adding on an `.html_safe` in the template is a redundant no-op, the results already are html-safe — both in the sense that they are marked `#html_safe`, and also that they truly are html-safe.

Now let’s look at another implementation of magicfy, completely wrong:

# WRONG!!!
def magicfy(value)
   "<span class='magic'>#{value}</span>"
end

Now under normal use, say we call `magicfy(“foo”)`, it returns the string we want, `<span class=”magic”>foo</span>`, but it’s not marked html_safe.  So if you display this in a Rails template, all the < and > will get escaped, “&lt;span…”, and you’ll see the literal HTML code in the rendered page, not what you want.

So maybe some poor coder says, oh, I’ll just throw in an .html safe in the template

WRONG! WRONG!
<%= magicfy("foo").html_safe %>

That appears to work, the problem is that the string isn’t neccesarily actually html-safe, because it doesn’t properly escape it’s input.

magicfy(“1 > 2”)

magicfy(“<script>…”)

You have made your code unsafe by just tagging on an `#html_safe` — depending on the arguments you might deliver invalid HTM (with un-escaped literal < or >), or if there’s user-input involved somewhere you might even be vulnerable to html injection.

The helper method itself has to be responsible for ensuring html-safety by escaping appropriate things, and then the helper method itself should mark the string .html_safe only once it’s done that — the same place that’s responsible for escaping has to be the place that marks html_safe, otherwise you’re just blindly marking html_safe without actually knowing it.

Using rails `content_tag` helpers is a great way to have html-safety just work appropriately. But you could do it yourself too (and sometimes have to), for instance:

# Safe, but it'd be easier to use content_tag
def magicfy(value)
   "<span class=\"magic\">#{ html_escape(value) }</span>".html_safe
end

If you do end up having to ensure html-safety and proper escaping yourself (always in the helper, never in the template), another really useful tool is the little-known safe_join helper.

If used properly, the Rails html-safety methods are pretty darn good at avoiding any possibility of HTML injection vulnerabilities or invalid HTML due to improperly unescaped chars. But `html_safe` method being called in template view code is, 99% of the time, a sign that you’re not doing things right and opening yourself up to problems. Code should never call html_safe on a string unless that code constructed the string and actually ensured it’s html-safety!

2 thoughts on “You never want to call html_safe in a Rails template

  1. Hmm, “never” is a strong word. On Rails 3, I have a Rails plain-text mailer template, and I’m using , and it’s getting HTML-escaped. Since this is a plain-text email template, the automatic HTML-escaping doesn’t really make sense here. I’m inclined to throw an html_safe onto @foo, but is there a better way?

  2. True, “never” is probably always too strong.

    I don’t have too much experience with Rails plain-text mailer templates, but I would have thought that if your template was `.txt.erb` instead of `.html.erb`, it wouldn’t be doing any html escaping at all. If it is anyway… I’d say that’s an unfortunate feature. And I’m not really sure the best way to deal with it, it might be what you’re doing. This post is definitely focused on HTML output, if your output isn’t going to a browser to be displayed as HTML, nothing here applies. :)

Leave a comment