25
28

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

RailsのXSS対策との付き合い方

Posted at

Rails 4では、普通に書けば意識しなくていい程度にXSS対策のエスケープが施されています。ただし、複雑なことをやろうとすれば引っかかる場面も出てきます。

TL;DR

  • 全般
  • できる限り、HTMLのコード生成にはヘルパーを使う
  • SafeBufferを左辺に置く
  • SafeBufferを文字列中に展開しない
  • モデル・ヘルパー内で
  • HTML出力するものはhtml_safeしておく
  • ビュー内で
  • raw<%==は原則使わない
  • html_safeは純粋リテラルだけに使う

2つの文字列型

Railsでは、ビューの出力側で特に工夫しなくても、HTMLを出力すべきものとエスケープしてから出力されるものが区別されています。

view.erb
<!-- こっちはエスケープ処理が入る -->
<%= 'a < b & c > d' %>

<!-- こっちはそのまま出力 -->
<%= link_to 'Qiita', 'http://qiita.com/' %>

こんな機能を実現するために、Railsでは通常のString型以外に、Stringを継承したActiveSupport::SafeBufferという型があって、後者ではHTMLエスケープなしで出力していいかを記録する機能が付いています1。ERBの出力では通常の文字列ならエスケープをかけて、SafeBufferはそのまま通す、ということをしています。

SafeBufferの生成

もちろんnewで作れなくもないのですが、通常は以下の2つのどちらかの方法で生成します。

  • '文字列'.html_safeというようにhtml_safeメソッドを呼び出す
  • html_escape('文字列')、あるいは短縮形のh('文字列')でエスケープする

前者については、html_safeだとマークするだけですので、中身が何であろうとそのまま出力することになります。タグを生成するにはこちらが必要ですが、下手にユーザー入力などの素性がわからない文字列に適用すればXSSを作りこんでしまいます。変数代入のない純粋なリテラルに適用するのならともかく、それ以外のものにビューでhtml_safeを乱発するのは避けたいものです。

なお、link_tocontent_tagといったHTML用のヘルパーがありますが、これらはSafeBufferを生成しています。基本的には、HTMLタグはこういったヘルパーで生成しましょう。

actionview/lib/action_view/helpers/tag_helper.rb
def content_tag_string(name, content, options, escape = true)
  tag_options = tag_options(options, escape) if options
  content     = ERB::Util.unwrapped_html_escape(content) if escape
  "<#{name}#{tag_options}>#{PRE_CONTENT_STRINGS[name.to_sym]}#{content}</#{name}>".html_safe
end

SafeBufferの演算

文字列は連結したりもよくしますが、SafeBufferを連結するとどうなるのでしょうか。

  • SafeBuffer + SafeBuffer…そのまま連結される(SafeBuffer
  • SafeBuffer + String…String側をエスケープした上で連結(SafeBuffer
  • String + SafeBuffer単なるStringとして両者を連結

最後のパターンでは、一度付いたhtml_safeが外れる結果、出力時にエスケープされて、HTMLタグがそのまま表示される結果となります。この形は避けるようにしましょう(SafeBufferを二重引用符の中で変数展開してしまっても同様なことになります)。ヘルパーなどでは、以下のような書き方も見られます。

HTML生成コード
def hoge
  str = ''.html_safe # バッファとして宣言
  str << foo
  str << bar(42)
  str
end

最後に

どうしてもうまく整わなければ、最後にビューで raw出力してしまう手段もなくはないのですが、まさにそれは最後の手段です。きちっとHTMLと文字列を意識してデータを生成すれば、ビューでの制御の必要はほぼなくなるでしょう。

外部リンク

  1. 破壊的変更を加えた場合など、SafeBufferでもhtml_safeでなくなることもあります。ただし、以下では特に断らない限りhtml_safeな場合についてを考えます。

25
28
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
25
28

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?