360

Suppose I have the following string

@x = "<a href='#'>Turn me into a link</a>"

In my view, I want a link to be displayed. That is, I don't want everything in @x to be unescaped and displayed as a string. What's the difference between using

<%= raw @x %>
<%= h @x %>
<%= @x.html_safe %>

?

Jamison Dance
  • 19,896
  • 25
  • 97
  • 99
grautur
  • 29,955
  • 34
  • 93
  • 128
  • 6
    Since nobody mentioned it I figured I'd also mention there's `<%== @x %>` which is an alias to `<%= raw(@x) %>` https://edgeguides.rubyonrails.org/active_support_core_extensions.html#output-safety – CTS_AE Feb 12 '19 at 20:21

7 Answers7

403

Considering Rails 3:

html_safe actually "sets the string" as HTML Safe (it's a little more complicated than that, but it's basically it). This way, you can return HTML Safe strings from helpers or models at will.

h can only be used from within a controller or view, since it's from a helper. It will force the output to be escaped. It's not really deprecated, but you most likely won't use it anymore: the only usage is to "revert" an html_safe declaration, pretty unusual.

Prepending your expression with raw is actually equivalent to calling to_s chained with html_safe on it, but is declared on a helper, just like h, so it can only be used on controllers and views.

"SafeBuffers and Rails 3.0" is a nice explanation on how the SafeBuffers (the class that does the html_safe magic) work.

Fábio Batista
  • 25,002
  • 3
  • 56
  • 68
  • 45
    I wouldn't say that `h` will ever be deprecated. Using `"Hi
    #{h@ user.name}".html_safe` is quite common and an accepted use.
    – maletor Jul 19 '11 at 23:21
  • 1
    @Maletor interesting usage, though I still think it falls into the "unusual" category. – Fábio Batista Jul 20 '11 at 14:52
  • 5
    String#html_safe actually returns an instance of ActiveSupport::SafeBuffer which wraps the original string and is #html_safe? . The original string does not become #html_safe? after calling #html_safe on it. – jmaxyz Apr 13 '12 at 15:57
  • 10
    Note that there is a subtle difference between `raw` and `html_safe` in practice: `raw(nil)` returns an empty string, while `nil.html_safe` throws an exception. – Van der Hoorn Aug 12 '13 at 09:11
  • so why the heck is html_safe stripping out hyphens from my urls? – jenson-button-event Mar 25 '14 at 11:53
  • 2
    `h` will not "revert" an html_safe declaration. When an string is `html_safe`, `h` will do nothing. – GuiGS May 26 '14 at 23:30
127

I think it bears repeating: html_safe does not HTML-escape your string. In fact, it will prevent your string from being escaped.

<%= "<script>alert('Hello!')</script>" %>

will put:

&lt;script&gt;alert(&#x27;Hello!&#x27;)&lt;/script&gt;

into your HTML source (yay, so safe!), while:

<%= "<script>alert('Hello!')</script>".html_safe %>

will pop up the alert dialog (are you sure that's what you want?). So you probably don't want to call html_safe on any user-entered strings.

the Tin Man
  • 158,662
  • 42
  • 215
  • 303
roasm
  • 1,373
  • 1
  • 8
  • 9
  • 108
    In other words, html_safe is not "please make this html safe", it's the opposite - it is you the programmer telling rails that "this string is html safe, promise!" – PaulMurrayCbr Jan 10 '14 at 04:50
  • actually I came here to figure out if it actually does _unescape_ or if it just makes a mark that it is not necessary _to_escape_. Quite a difference. Oh well, off to read the source code then. – Simon B. Mar 31 '15 at 12:15
  • 1
    The concept of "html_safe" is just a meta flag on the string. Marking something as `html_safe` does *not* escape *nor* unescape. While the end result of marking something as *not* HTML safe, and then using the implicit escaping of the ERB <%= tag, might be the same as unescaping data and then re-escaping it on output, functionally it's doing neither. Kind of like the difference of (6 * -1 * -1), vs. 6. – Ben Zittlau Aug 22 '19 at 18:23
52

The difference is between Rails’ html_safe() and raw(). There is an excellent post by Yehuda Katz on this, and it really boils down to this:

def raw(stringish)

  stringish.to_s.html_safe

end

Yes, raw() is a wrapper around html_safe() that forces the input to String and then calls html_safe() on it. It’s also the case that raw() is a helper in a module whereas html_safe() is a method on the String class which makes a new ActiveSupport::SafeBuffer instance — that has a @dirty flag in it.

Refer to "Rails’ html_safe vs. raw".

stevendaniels
  • 2,992
  • 1
  • 27
  • 31
Pankhuri
  • 958
  • 8
  • 5
39
  1. html_safe :

    Marks a string as trusted safe. It will be inserted into HTML with no additional escaping performed.

    "<a>Hello</a>".html_safe
    #=> "<a>Hello</a>"
    
    nil.html_safe
    #=> NoMethodError: undefined method `html_safe' for nil:NilClass
    
  2. raw :

    raw is just a wrapper around html_safe. Use raw if there are chances that the string will be nil.

    raw("<a>Hello</a>")
    #=> "<a>Hello</a>"
    
    raw(nil)
    #=> ""
    
  3. h alias for html_escape :

    A utility method for escaping HTML tag characters. Use this method to escape any unsafe content.

    In Rails 3 and above it is used by default so you don't need to use this method explicitly

Deepak Mahakale
  • 22,834
  • 10
  • 68
  • 88
28

The best safe way is: <%= sanitize @x %>

It will avoid XSS!

3

In Simple Rails terms:

h remove html tags into number characters so that rendering won't break your html

html_safe sets a boolean in string so that the string is considered as html save

raw It converts to html_safe to string

user3118220
  • 1,438
  • 12
  • 16
  • 2
    The answer is correct: h is html_escape ... [from the Rails code base](https://github.com/rails/rails/blob/6-0-stable/activesupport/lib/active_support/core_ext/string/output_safety.rb#L20-L25) – notapatch Oct 03 '19 at 14:29
0

Short and Simple

Let's assume we can't trust user input.

Bad:

user_input.html_safe # asking for trouble

Good:

user_input.html_escape # or

h(user_input) # in some view

Inputs we control:

trusted_input_only.html_safe

that should be fine. but be careful what your trusted inputs are. They must only be generated from your app.

BenKoshy
  • 33,477
  • 14
  • 111
  • 80