🔧

[Laravel] HTML をエスケープするしくみ

Laravel 5.8 を想定。

通常は HTML がエスケープされる

以下のように、変数に HTML が文字列として入っているとする。

$html = '<p>sentence</p>';

これを {{ }} で表示すると

{{ $html }}

HTML がエスケープされてから表示される。

sentence

これは、XXS を防ぐために HTML や JS を排除すべきだから。

HTML として表示にするには

{!! !!} が用意されているので、これを使う。

{{ $html }}
{!! $html !!}

出力結果はこうなる。

sentence
<p>sentence</p>

{{ $html }} はただの文字列として、{!! $html !!} は HTML として表示される。

どういうしくみなのか

{{ }}{!! !!} のしくみを調べてみる。

コンパイル済みの view を見ると、{{ }}{!! !!} はそれぞれ以下のようにそれぞれ変換されている。

  • {{ $html }} ==> <?php echo e($html); ?>
  • {!! $html !!} ==> <?php echo $html; ?>

{{ $html }}$html をエスケープする関数 e() を通して変数の値を表示していることがわかる。

ちなみに、{{ }}regular echo{!! !!}raw echo と呼ばれる。

Q. エスケープって数値文字参照に変えることでは?

「エスケープする」とは「記号を文字参照に変換する」ことだから、

<p>sentence</p>

&lt;p&gt;sentence&lt;/p&gt

という文字列に変換されるはず。 それなのにブラウザに <p>sentence</p> と表示されるのは、ブラウザが文字に変換しているから。

ただし例外がある

{{ }} なら何でもエスケープされるわけではない。 Blade には、いくつかの例外が存在する。

{{ csrf_field() }}
とか
{{ $posts->render() }}
もしくは
{{ $posts->links() }}

これらは {{ }} で囲っているにもかからわず、HTML として出力される。本来なら {!! !!} とするべきなのに。

なぜ HTML として表示されるのか、それは {{ }} で呼ばれる e() に例外処理が追加されたから (Laravel 5.1 以降)1

  function e($value)
  {
+     if ($value instanceof Htmlable) {
+         return $value->toHtml();
+     }
      return htmlentities($value, ENT_QUOTES, 'UTF-8', false);
  }

Htmlable のインスタンスであれば、そのまま出力されるようになっていることがわかる。 Htmlable のインスタンスを生成するにはサーバ側で処理が必要になるので、フォームにいくら HTML を入力しようが、その値は string にしかならない。 よって、Htmlable を継承しているなら例外対応しても問題ない。

実際、csrf_field() を見ると、HtmlString のインスタンスであることがわかる。

function csrf_field()
{
    return new HtmlString('<input type="hidden" name="_token" value="'.csrf_token().'">');
}

$posts->render() も同様。

public function render()
{
    if ($this->hasPages()) {
        return new HtmlString(sprintf(
            '<ul class="pagination">%s %s %s</ul>',
            $this->getPreviousButton(),
            $this->getLinks(),
            $this->getNextButton()
        ));
    }
    return '';
}

おまけ:{{{ }}} もある

今は使われていないけど、以前は {{{ }}} もあったらしい。 Laravel 4 以前は、{{ }} がエスケープしない、{{{ }}} がエスケープする表示方法だったから2

つまり、今の記法と比べるとこんな感じ。

エスケープしない {{ }} {!! !!}
エスケープする {{{ }}} {{ }}

ちなみに、{{{ }}} は後方互換のために、今も使えるとのこと3escaped echo という名前で定義されている。