環境
- Rails 8.0
safe_joinサンプルコード
Railsのビューで複数の文字列をHTMLタグで結合したい場面がある。たとえば複数行のテキストを<br>区切りで表示したいとき、素朴に書くとこうなる。
lines.join("<br>").html_safe
これで動くにはするが、html_safeを使うと全体のHTMLエスケープが無効になり、linesの中にユーザー入力が混入していると XSS の温床になる。そこで使うのがsafe_joinだ。
module ApplicationHelper def make_sample_lines lines = [] lines << "りんご" lines << "バナナ" lines << "ぶどう" safe_join(lines, tag.br) end end
<p><%= make_sample_lines %></p>
safe_join使うメリットとは? html_safeでもよくね?
safe_join の核心はここだ。各要素を個別にエスケープしつつ、セパレータのHTMLはそのまま通すという挙動にある。
lines = ["<script>alert('xss')</script>", "バナナ"] safe_join(lines, tag.br) # => "<script>alert('xss')</script><br>バナナ"
html_safeを使った場合と比較してみる。
# NG: 全体が unsafe 扱いのまま lines.join(tag.br) # NG: html_safe で全体のエスケープを無効化 → XSSリスク lines.join("<br>").html_safe # OK: 各要素はエスケープ、セパレータはそのまま出力 safe_join(lines, tag.br)
html_safeを一度つけてしまうと「この文字列は安全だとRailsが信用する」という意味になり、その中にユーザー入力が含まれていても無条件にHTMLとして出力される。一方safe_joinは要素ごとにエスケープ処理を適用するため、セパレータにtag.brのようなHTMLタグを渡しても安全に扱える。
つまり「動けばいい」ならhtml_safeでも通るが、ユーザー入力や外部データを扱うならsafe_join一択だ。