Ruby on Rails

【Rails】ViewのERB内でHTMLタグをエスケープせずに出力する方法

業務でテーブルの値を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オプション付きで使うで大丈夫だと思います。

ABOUT ME
sakai
東京在住の30歳。元々は車部品メーカーで働いていてましたが、プログラミングに興味を持ちスクールに通ってエンジニアになりました。 そこからベンチャー → メガベンチャー → 個人事業主になりました。 最近は生成 AI 関連の業務を中心にやっています。 ヒカルチャンネル(Youtube)とワンピースが大好きです!