Skip to main content
The Kai Way

ActionView Safe Buffer

为了提高安全性,ActionView的模版在Rails 3中实现了名为SafeBuffer用来减少被XSS攻击的风险。

本文是Inspect Rails的一部分, Inspect Rails是由我正在编写的讲解Rails内部实现与设计的一本书, 欢迎阅读

XSS攻击 #

XSS,全称为Cross-site Scripting,中文叫跨站脚本攻击。这是通过对目标网页注入脚本(最常见是JavaScript,也可以是VBScript等),然后通过这段脚本来盗取用户cookies或做跨站提交等。

要防止这种攻击,Rails开发者必须非常小心地处理用户输入的内容,本篇讲到SafeBuffer就是帮助开发者减小被攻击的风险。

HTML Safe #

在ActionView的Template中,默认的内容是HTML Unsafe的,HTML Unsafe的内容被拼接时会先用ERB::Utils.html_escape方法先处理一遍。只有两种才会被认为是HTML Safe的

这里可能会让人出乎意料的是,SafeBuffer的实现放在ActiveSupport的String Extention里,具体定义文件在active_support/core_ext/string/output_safety.rb

SafeBuffer被定义为String的子类,与普通的String不同是SafeBuffer的html_safe属性为True。

module ActiveSupport #:nodoc:
  class SafeBuffer < String
    def initialize(*)
      @html_safe = true
      super
    end
    # other methods
  end
end

另外,对于其他的对象,通过打开类的方式将Object的html_safe设置为False,而Numeric被设置为True。具体定义如下

class Object
  def html_safe?
    false
  end
end

class Numeric
  def html_safe?
    true
  end
end

我们知道String的内容是可变的,同样SafeBuffer的内容也是可变的。出于安全性考虑SafeBuffer会将产生新对象或修改内容本身的方法,比如capitalizegsub等等,都替换为结果是HTML Unsafe的字符串

class_eval <<-EOT, __FILE__, __LINE__ + 1
  def #{unsafe_method}(*args, &block)
    to_str.#{unsafe_method}(*args, &block)
  end

  def #{unsafe_method}!(*args)
    @html_safe = false
    super
  end
EOT

比如替换后的capistalize方法是

def capitalize(*args, &block)
  to_str.capitalize(*args, &block)
end

def capitalize!(*args)
  @html_safe = false
  super
end

稍微解释一下方法替换的意义,在非bang方法中,先调用to_str就将原字符串转化为普通的String,由于除了SafeBuffer外的对象都是unsafe的,通过这么转化本来HTML Safe的内容又变回了HTML Unsafe的状态。

当需要将内容标记为html safe状态的时候,可以调用html_safe方法,这个方法的原理就是构造一个新的SafeBuffer对象,代码如下

class String
  def html_safe
    ActiveSupport::SafeBuffer.new(self)
  end
end

接口 #

基本上所有模版语言都放出了,一些回调接口让开发者可以替换掉原有的Buffer实现。ActionView里定义的Template Handler就完成了模版语言Buffer实现的替换,比如这里的对Erb的替换

一些第三方的模板语言,比如Haml直接集成了SafeBuffer,Slim通过其依赖的Temple也集成了SafeBuffer。

参考 #