💡

simple_formatを使ってテキストエリアに入力した文章を改行表示する - サニタイズとエスケープの違いを理解する

に公開

ユーザーがテキストエリアに入力した文章。改行したはずなのに、表示してみると改行が消えていた…
HTMLでは、改行コード(\n)は単なるスペースとして扱われるため、意図した通りの表示になりません。
Railsのヘルパーメソッドsimple_formatを使って入力されたテキストをHTML形式に変換することで、HTMLに改行を反映させることができます。

調査

テキストエリアに入力した改行を含むテキストはどう保存されているか確認します。

文章1

文章2


文章3
> Article.last
=> 
#<Article:0x0000ffff6432db88
 body: "文章1\n\n文章2\n\n\n文章3",

HTMLでは、改行文字(\n)は表示時に空白文字として解釈されます。つまり、与えられたテキストは

文章1 文章2 文章3

と、単一の改行文字(\n)は単なるスペースとして扱われ、複数の改行(\n\n)も単一のスペースとして解釈されます。

HTMLで、改行を反映させるには、<br> タグを使用する、<p> タグで段落を分けるの方法があります。
また、CSSで"white-space: pre-wrap"クラスを利用する方法もあります。 https://developer.mozilla.org/en-US/docs/Web/CSS/white-space
simple_formatを利用することで改行コード(\n)を<br>タグ、<p>タグに変換することでHTMLに改行を反映させることができます。

simple_formatを使う

simple_formatの挙動

https://api.rubyonrails.org/v8.0/classes/ActionView/Helpers/TextHelper.html#method-i-simple_format

simple_formatはフォーマットルールに従ってテキストをHTMLに変換します。

  • \n\nは段落と見なされ<p>タグに変換する
  • \nは改行と見なされ<br>タグに変換する。
# \nは<p>タグ、<br>タグに変換される
simple_format ("あいうえお\n\nかきくけこ\nさしすせそ")
=> "<p>あいうえお</p>\n\n<p>かきくけこ\n<br />さしすせそ</p>"
  • デフォルトで入力をサニタイズしますが、エスケープは行いません。つまり、HTMLタグはページに表示されますが、悪意のあるコードはすべて削除されます。
    • サニタイズ: HTMLから悪意のある部分(<script>タグなど)を削除する処理です。安全なタグ(<a>, <b>など)は残します。
    • エスケープ: HTMLの特殊文字(<, >, &など)を、文字として表示される安全な形式(<, >, &など)に変換する処理です。これにより、ブラウザがそれらをタグとして解釈するのを防ぎます。
# aタグを含むテキストを入力する場合はaタグとして表示される
simple_format('<a href="http://example.com/">Example</a>')
=> "<p><a href=\"http://example.com/\">Example</a></p>"

# 悪意のあるコードはサニタイズされる
> simple_format('<a href="javascript:alert(\'no!\')">Example</a>')
=> "<p><a>Example</a></p>"
  • すべてのコンテンツをエスケープする場合(HTMLタグとして扱わずに文字列として扱いたい場合)は、hメソッドを使う
> simple_format h('<a href="http://example.com/">Example</a>')
=> "<p>&lt;a href=\"http://example.com/\"&gt;Example&lt;/a&gt;</p>"```

hの挙動 - XSS対応

https://docs.ruby-lang.org/ja/latest/method/ERB=3a=3aUtil/m/h.html

hメソッドは、XSS攻撃を防ぐためにユーザーが入力した悪意のあるコード(<script>alert("XSS")</script>)を実行可能なコードではなく、文字として表示される安全な形式に変換します。
例えば、zennのエディタでは、<script>alert("XSS")</script>を入力してもエスケープ処理されて文字列として表示されています。

> h('<script>alert("XSS")</script>')
=> "&lt;script&gt;alert(&quot;XSS&quot;)&lt;/script&gt;"

まとめ

ユーザーが入力したテキストの改行を保持して表示するには、simple_formatを使う。
hメソッドの使い分けはユーザーの入力値に対する扱いから検討する。

  • simple_format @article.body: ユーザーが入力したHTMLタグの一部(<a>など)を意図的に有効にしたい場合。
  • simple_format h @article.body: ユーザーの入力内容をすべて安全なテキストとして扱い、タグとして解釈させたくない場合。
コード サニタイズ エスケープ \nの表示
@article.body なし なし \nは空白文字として扱う
simple_format @article.body <script>タグなどを削除する なし \nは<br>タグ、<p>タグに変換
simple_format h @article.body エスケープによりタグ無効化 あり。<や>などの特殊文字が&lt;&gt;にエスケープされる \nは<br>タグ、<p>タグに変換

Discussion