One of the places where web applications are vulnerable to XSS attacks is when they display input from users. It is therefore important to escape any user-generated input that is displayed and in Rails applications this is usually done by making use of the
h
method.- <%= h comment.content %>
Using the h method to escape output.
In Rails 3, however, output is escaped automatically so there’s no need to put the h
method in your views and in this episode we’ll show you how Rails 3 handles the escaping of output.To demonstrate output escaping we’re going to use a simple blogging application written in Rails 3. In this app we have articles and each article has a number of comments associated with it. To test how the comment system copes with an attempt at cross-site scripting we’ll enter
<script>alert('I steal cookies!')</script>
in each field in the comments form and submit our evil comment.h
./app/views/comments/_comment.html.erb
- <div class="comment">
- <strong><%= link_to comment.name, comment.url %></strong>
- <p><%= comment.content %></p>
- </div>
What happens, thought, if we’re transitioning an application from Rails 2 and have our output escaped with
h
? We can find out by escaping the output in the partial above and reloading the page./app/views/comments/_comment.html.erb
- <div class="comment">
- <strong><%= link_to h(comment.name), comment.url %></strong>
- <p><%= h comment.content %></p>
- </div>
h
method it will escape the script tag only the once.It might seem that the h method does nothing in Rails 3 but that’s not that case. It does still have a purpose and we’ll show you later what that is. But before we do that we’ll look at a related feature. While it’s great to have the output escaped automatically there might be times when we want to display the raw content. If we trust the content that the user enters, say they’re an administrative user, and we want to display exactly what they enter then we can use the new
raw
method to do that.- <div class="comment">
- <strong><%= link_to comment.name, comment.url %></strong>
- <p><%= raw comment.content %></p>
- </div>
raw
method. But how does it work? Rails 3 seems to be pretty clever about when to escape content and when not to. We’ll show you now how that works.We’ll demonstrate escaping in the console, which in Rails 3 we can invoke with the
rails c
command.$ rails c Loading development environment (Rails 3.0.0.beta) ruby-1.9.1-p378 >Rails 3 has a concept of HTML-safe strings. This means that we can check if any string is safe to output as HTML by calling the new method
html_safe?
on it.> "foo".html_safe? => falseWe can mark a string as being HTML-safe by calling the
html_safe
method on it.> safe = "safe".html_safe => "safe" > safe.html_safe? => trueNo actual escaping goes on here. All that happens is that a boolean property is set against a string to determine whether it should be escaped before being output.
So how does this apply in our view template? Well, Rails looks at each piece of output and sees if it’s marked as HTML-safe. If it’s not then it will be automatically escaped when it is output to the view. If it is safe then it is passed through without being escaped. If we use the
h
method to escape a string it will perform the escaping and mark the string as HTML-safe. This means that Rails 3 will see that the string is safe and not escape it again.When the
raw
method is used on a string it will be marked as HTML-safe but not escaped, ensuring that the string’s content is passed to the output without being changed.This is an important thing to understand when you’re using helper methods. We’ll explain this by creating a helper method called
strong
that wraps whatever is passed to it in <strong>
tags. We’ll use it in our template like this:/app/views/comments/_comment.html.erb
- <div class="comment">
- <%= strong link_to(comment.name, comment.url) %>
- <p><%= raw comment.content %></p>
- </div>
strong
method in the ApplicationHelper
:/app/helpers/application_helper.rb
- module ApplicationHelper
- def strong(content)
- "<strong>#{content}</strong>"
- end
- end
<strong>
tag here and this is because our helper method doesn’t create an HTML-safe string.There are two simple rules that need to be need to be followed when working with helper methods that return HTML. Firstly, we need to ensure that any strings returned are marked as HTML-safe.
/app/helpers/application_helper.rb
- module ApplicationHelper
- def strong(content)
- "<strong>#{content}</strong>".html_safe
- end
- end
<strong>
tag being escaped but by doing that the content between the tags will not be escaped. We can solve this by escaping the content with h
:/app/helpers/application_helper.rb
- module ApplicationHelper
- def strong(content)
- "<strong>#{h(content)}</strong>".html_safe
- end
- end
h
and then mark the resulting string as html_safe
it will be rendered properly. If we reload our comments page now we’ll see that the <strong>
tag hasn’t been escaped but that the content of the second comment, which includes the potentially dangerous JavaScript is escaped.h
, this reducing the changes of your application being a victim of cross-site scripting.
No comments:
Post a Comment