📜 ⬆️ ⬇️

XSS protection in Rails 3

Most likely you already know that, by default, Rails 3 adds protection against XSS attacks. This means that from now on you will never have to manually filter user input using the h helper, because rails will always do it for you.

However, everything is not as simple as it seems at first glance. Consider the following code:
<strong></strong>!

<%= tag(:p, some_text) %>
<%= some_text %>

In the example above, we have several different cases involving the use of HTML tags. In the first case, Rails should not filter the <strong> that surrounds the word , because The result is clearly not what the user entered. In the second case, Rails should filter some_text inside the <p> (but not the whole <p> ). Finally, in the third case, some_text in some parent tag must be filtered.

In case some_text looks like the result will be:
<strong></strong>!

<p>&lt;script&gt;evil_js&lt;/script&gt;</p>
&lt;script&gt;evil_js&lt;/script&gt;
In order for this to work everywhere in Rails applications, we have implemented a new approach called html_safe . So, if the string is html_safe (as determined by calling the html_safe? method html_safe? On the string), the ERB leaves it untouched. If the string is not html_safe , the ERB will filter it before inserting it into the page.
')
def tag(name, options = nil, open = false, escape = true)
"<#{name}#{tag_options(options, escape) if options}#{open ? ">" : " />"}".html_safe
end
Here, Rails creates the tag, teg_options filter the contents, and then marks the entire body of the tag as safe. As a result, <p> and </p> will remain untouched, while user-inserted content will be subject to filtering.

In the first implementation, in the Koz rails-xss plugin, the specified conditions were fulfilled by adding a special flag to all lines. The Rails application marked the lines as safe, while Rails itself redefined the + and << methods so that, if the line was changed, it marked the resulting one accordingly.

However, during my last performance test of rails, I noticed that redefining each string concatenation results in a rather severe drop in performance. Moreover, this drop depended linearly on the number of inserts <%= %> in the template, i.e. large templates did not absorb the cost (as if it were one call <%= %> per template), but only increased it.

Thinking more about the problem, it became clear (as Koz, Jeremy and Evan Phoenix from Rubunius confirmed later) that we could implement exactly the same features in a more productive way, with even less impact on the Ruby API. Since the problem itself is quite complicated, I will not go into details describing the old implementation, but I will explain how to use the new XSS protection. If you happened to use the Koz plugin earlier or you are already working with preliminary releases of Rails, you will notice that this commit doesn’t change much.

Safe buffer


In Rails 3, the ERB buffer is an instance of ActiveSupport::SafeBuffer . SafeBuffer is inherited from String 'a, overriding + , concat and << in such a way that:
Calling html_safe on a simple string returns a SafeBuffer wrapper. Since SafeBuffer inherited from the timeline, Ruby creates a wrapper extremely efficiently (only by sharing the internal storage char* ).

As a result of this implementation, I started to see this way of recording:
buffer << other_string.html_safe
Here, Rails creates a new SafeBuffer for other_string , then passes it to the << source SafeBuffer 's method, which (method) checks if the new SafeBuffer safe. For such cases, safe_concat was written - a new method for the buffer, which uses the original concat method, eliminating the need to create a new SafeBuffer and check it for security.

Similarly, concat and safe_concat in ActionView are proxies for concat and safe_buffer over the buffer, so you can use safe_concat in the helper if you have HTML text that needs to be combined with the buffer without checking and filtering.

ERB internally uses safe_concat everywhere on the template outside the <% %> safe_concat <% %> tags. This means that, in my today's commit, the XSS protection code does not affect the performance in such cases (i.e., scanning, in fact, all your plain text).

Finally, ERB now knows about the raw helper, so if you write something like <%= raw some_stuff %> , ERB quietly uses safe_concat , skipping the creation of SafeBuffer 'a and html_safety checks.

findings


In short, “XSS protection” means the following:
For comparison, in the original XSS implementation, it affected every concatenation or + for strings; influenced even if the application used raw helper, or any other concatenation outside <% %> inside the template.

However, I would like to express my personal thanks to Michael Koziarsky, who provided a draft implementation of his idea. She worked, demonstrated the concept, and the community tested it to the full. In the end, she gave a good start.

Source: https://habr.com/ru/post/82726/


All Articles