#204 XSS Protection in Rails 3
- Download:
- source codeProject Files in Zip (156 KB)
- mp4Full Size H.264 Video (11.8 MB)
- m4vSmaller H.264 Video (8.63 MB)
- webmFull Size VP8 Video (21 MB)
- ogvFull Size Theora Video (15.1 MB)
回顾第27集[观看, 阅读],我们详细介绍了跨站脚本攻击。这是每个网站开发人员都应该掌握的一门重要课程。在web应用中,当网页显示用户的输入内容时易受到xss攻击。因此,转义用户自行输入内容是很重要的,在Rails应用中,通常使用h
方法来完成。
<%= h comment.content %>
使用h方法转义输出
在Rails 3中,输出可以自动转义,因此,无需在你的视图中再加入h
方法。在此集中,我们将向你展示Rails3如何处理输出转义。
为了演示输出转义机制,我们将使用一个用Rails 3 编写的简单的博客应用。在这个应用中,我们拥有很多文章,而且每篇文章都有很多相关的评论。为测试评论系统如何应对跨站脚本攻击的尝试,我们在评论表单的每个字段中都输入<script>alert('I steal cookies!')</script>
,然后提交我们这个恶意的评论。
当我们添加的这个评论在网页上显示出来之后,可以看到Rails 3已经自动转义了上述我们所提交的字段中的HTML标签。现在我们看看Rails是如何做到的。
显示每条评论的代码被包含在一个局部模板中,但是,如果你查看这些代码,你会发现并没有任何输出使用了h
转义。
<div class="comment"> <strong><%= link_to comment.name, comment.url %></strong> <p><%= comment.content %></p> </div>
在Rails2中这意味着将会显示alert信息,但是所有在局部模板中的输出都会被Rails
3正确的转义,即使是通过了诸如link_to之类的helper方法也是如此,所以这里不需使用h
方法。
想想,如果我们正在转换一个Rails 2的应用,其中的输出都使用了h
进行转义,会发生什么?通过转义前述局部模板的输出并重载该页我们可以找出答案。
<div class="comment"> <strong><%= link_to h(comment.name), comment.url %></strong> <p><%= h comment.content %></p> </div>
当我们重新装载页面,可以看到页面看起来与上次是一样的,输出并没有被重复转义。Rails很聪明;即便我们使用了h
方法,它也只会进行一次脚本标签的转义。
看起来似乎h
方法在Rails 3中没什么用
,其实不然。它仍然有一个作用,稍后我们会向你演示是什么。在这之前,我们要先看一个相关特性。也许有时当我们想显示原始内容时,能自动转义输出是很好的。但如果我们信任用户输入的内容,比如说他们是管理员,并且我们想精确的显示他们输入了什么,就可以使用新的raw
方法来实现。
<div class="comment"> <strong><%= link_to comment.name, comment.url %></strong> <p><%= raw comment.content %></p> </div>
如果现在我们重新载入页面,我们在评论栏输入的JavaScript就会被执行。
所以,在Rails3中,无论何时,当我们不希望内容被HTML转义时,我们就可以使用raw
方法。但是这是如何实现的?在何时转义内容和何时不转义内容方面,Rails3看起来相当的聪明。现在我展示给你们看它是怎样实现的。
我们将在console中演示转义功能,在Rails 3中我们可以用rails c
命令来唤出它。
$ rails c Loading development environment (Rails 3.0.0.beta) ruby-1.9.1-p378 >
Rails 3 引入了HTML安全字符串的概念。这意味着可以通过调用新的html_safe?
方法来检查字符串作为HTML输出是否安全。
> "foo".html_safe? => false
我们可以通过调用html_safe
方法来标记一个字符串是HTML安全的。
> safe = "safe".html_safe => "safe" > safe.html_safe? => true
这里实际上没有进行转义。所发生的一切只是对一个字符设置了一个布尔属性来指示它在输出前是否要被转义。
那么这是如何应用在我们的视图模板中的呢。Rails会检查每个输出看它是否被标记为HTML安全的。如果不是,在输出到页面时它将被自动转义。反之,它将直接通过而不会被转义。如果我们使用h
方法来转义一个字符串,该字符串将会被执行转义动作,同时被标记为HTML安全的。这就意味着Rails 3将会认为该字符串是安全的而不会再次转义它。
当在一个字符串上使用raw
方法时,它将被标记为HTML安全的但不被转义,从而确保该字符串的内容会不被修改的传递到输出。
当你使用helper方法时,理解这些是非常重要的。我们将会创建一个名叫strong
的helper方法来解释这个问题,这个方法会将任何传递给它的东西包装在 <strong>
标签里。我们将在模板中像这样使用它:
<div class="comment"> <%= strong link_to(comment.name, comment.url) %> <p><%= raw comment.content %></p> </div>
我们在 ApplicationHelper
中创建了strong
方法:
module ApplicationHelper def strong(content) "<strong>#{content}</strong>" end end </pre> <p>当重载页面时,我们可以看到它并没有按照我们预想的方式工作。</p> <div class="imageWrapper <img width="815" height="552" alt="The strong tag in our helper method is escaped." src="http://railscasts.com/static/episodes/asciicasts/E204I04.png"/> </div> <p>Rails 3的自动转义机制会转义掉<code><strong></code>标签,这是因为我们辅助方法没有创建一个HTML安全的字符串。</p> <p>当使用返回结果是HTML代码的辅助方法时,我们必须遵循两条简单的原则。第一,我们必须保证任何返回字符串被标记为HTML安全的。</p> ``` /app/helpers/application_helper.rb <p>这修正了 <code><strong></code>标签被转义的问题,但这么做会导致这个标签内的内容不被转义。我们可以通过用<code>h</code>方法转义这些内容来解决这个问题:</p> ``` /app/helpers/application_helper.rb module ApplicationHelper def strong(content) "<strong>#{h(content)}</strong>".html_safe end end
所以只要我们记得使用h
方法来转义所有的输入,然后把结果字符串标记为html_safe
的,它就会正确的呈现。如果现在我们重载评论页面,可以看到<strong>
标签没有被转义,但是第二条评论中包含了有潜在危险的JavaScript的内容被转义了。
以上就是本集的主要内容。自动转义机制是Rails 3视图中的一个广受欢迎的增强。它使得我们不再需要记住用h
h来转义每一处输出,这样就减少了您的应用成为跨站脚本攻击的受害者的机会。