【Rails】タグのエスケープ & 連続した改行の反映
結論
safe_joinを使えば、タグをエスケープしてXSS攻撃を防ぎつつ、連続した改行も反映される。
<% text = "1行目\n\n\n2行目<script>alert()</script>" %> <%= safe_join(text.split("\n"), tag.br) %> # 1行目 # # # 2行目<script>alert()</script>
結論に至るまでの道のり
その1
<% text = "1行目\n\n\n2行目<script>alert()</script>" %> <%= text.gsub("\n", '<br>').html_safe %> # 1行目 # 2行目
- ❌タグのエスケープ
- ❌連続した改行の反映
連続した改行は反映されないし、alert()
が実行されてしまう。
その2
<% text = "1行目\n\n\n2行目<script>alert()</script>" %> <%= html_escape(text).gsub("\n", '<br>').html_safe %> # 1行目 # # # 2行目<script>alert()</script>
- ✅タグのエスケープ
- ✅連続した改行の反映
しかし、これだとrubocopのRails/OutputSafetyで怒られる。
またRails API:String#html_safeにもあるように、html_safe()
をユーザー入力に対して使うのは非推奨とされている。
その3
<% text = "1行目\n\n\n2行目<script>alert()</script>" %> <%= simple_format(html_escape(text)) %> # 1行目 # # 2行目<script>alert()</script>
- ✅タグのエスケープ
- ❌連続した改行の反映
改行が1つにまとめられてしまう。
その4
<% text = "1行目\n\n\n2行目<script>alert()</script>" %> <%= safe_join(text.split("\n"), tag.br) %> # 1行目 # # # 2行目<script>alert()</script>
- ✅タグのエスケープ
- ✅連続した改行の反映
結論、タグのエスケープと連続した改行の反映を行いたい場合は、safe_joinを使おう。
【参考】
- Railsガイド:Rails セキュリティガイド - 7.3 クロスサイトスクリプティング(XSS)
- Railsガイド:Active Support コア拡張機能 - 5.1.2 安全な文字列
- Rails: ビューのHTMLエスケープは#link_toなどのヘルパーメソッドで解除されることがある
- String#gsub
- ERB::Util.#html_escape
- simple_format - ActionView::Helpers::TextHelper
- [Rails]ビューで配列を改行するなら脆弱なjoinではなくsafe_joinにしよう
- tag - ActionView::Helpers::TagHelper
- Rails: 5.1以降のtagヘルパー記法はcontent_tagより便利
- Railsでタグをエスケープしつつ、改行を含む複数行のTextを表示したい - Qiita
- Rails – erbファイルでの改行の反映 – TauStation