業務でテーブルの値をHTMLタグを含めて成形し出力したいというケースがあり、今回は備忘録としてそのあたりを書いていきたいと思います。
ERBのタグ内にHTMLタグを入れる
<%タグの中でHTMLタグをそのまま入れる
ただ単純に<%タグにHTMLタグを挿入するだけでは、エスケープされてしまいました。
# コントローラー
@html = "#{@article.title}<br>#{@article.content}"
# ビュー
<%= @html %>
# 出力
記事1<br>a
Railsガイドによると、「Active Supportには「(html的に) 安全な文字列」という概念があり、安全な文字列であると判断された場合には、「実際にエスケープされているかどうかにかかわらず」その文字列は信頼されます。」とあります。
以下はRailsガイド抜粋の例です。
文字列はデフォルトでは「unsafe」とマークされます。
"aa".html_safe?
=> false
与えられた文字列にhtml_safeメソッドを適用することで、安全な文字列を得られます。
"aa".html_safe.html_safe?
=> true
ということなので、今回もhtml_safeメソッドを使うことで、出力することができそうです。
#html_safeメソッドを使って出力する (NG)
# コントローラー
@html = "#{@article.title}<br>#{@article.content}"
# ビュー
<%= @html.html_safe %>
# 出力
記事1
a
出力はできているが、これではXSSに対して脆弱なのでNGです。
例えば、悪意あるユーザが@article.titleに<script>alert(‘XSSできます’);</script>のようにスクリプトを登録していた場合、そのスクリプトを実行できてしまいます。
<%= @html.html_safe %>
これはraw や <%== を使って出力した場合も同じです。これらは内部でhtml_safeメソッドを使っています。
#sanitizeメソッドを使って出力する
じゃあどうすればいいかという話しですが、結論sanitizeメソッドを使います。sanitizeメソッドはtagsやattributesオプションで指定されていないタグや属性をすべて除去します。
実際にきちんと削除されるか試してみます。
下の例では、tagsオプションでbrタグを指定しているので、きちんと改行がされています。
# コントローラー
@html = "#{@article.title}<br>#{@article.content}"
# ビュー
<%= sanitize @html, tags: %w(br) %>
# 出力
記事1
a
# 出力(HTML)
記事1
<br>
a
aタグやscriptタグを入れて試してみます。
指定していないタグが除去されているのが分かるかと思います。
# 【aタグの場合】
# コントローラー
@html = "#{@article.title}<a href='https://example.com'>#{@article.content}</a>"
# ビュー
<%= sanitize @html, tags: %w(br) %>
# 出力
# 記事1a
# 出力(HTML)
# 記事1a
# 【scriptタグの場合】
# コントローラー
@html = "#{@article.title}<script>https://~~</script>#{@article.content}"
# ビュー
<%= sanitize @html, tags: %w(br) %>
# 出力
# 記事1https://~~a
# 出力(HTML)
# 記事1https://~~a
このsanitizeメソッドをtagsオプションなしで使うと、安全なタグとみなされたタグに対してはエスケープされません。
# コントローラー
@html = "#{@article.title}<strong>テスト</strong>#{@article.content}"
# ビュー
<%= sanitize @html %>
# 出力
# 記事1テストa
# 出力(HTML)
# 記事1<strong>テスト</strong>a
安全なタグかどうかはこちらで定義されており、確認できます。
https://github.com/rails/rails-html-sanitizer/blob/master/lib/rails/html/sanitizer.rb#L108
とはいえ、予め挿入されうるタグが分かっている場合は、tagsオプションを使ってそれ以外のタグはエスケープすべきでしょう。
Viewで<%タグの中にHTMLタグを埋め込みたいときはsanitizeメソッドをtagsオプション付きで使う
まとめ
まとめとしては、Viewで<%タグの中にHTMLタグを埋め込みたいときはsanitizeメソッドをtagsオプション付きで使うで大丈夫だと思います。