[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>
は
<p>sentence</p>
という文字列に変換されるはず。
それなのにブラウザに <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。
つまり、今の記法と比べるとこんな感じ。
昔 | 今 | |
---|---|---|
エスケープしない | {{ }} |
{!! !!} |
エスケープする | {{{ }}} |
{{ }} |
ちなみに、{{{ }}}
は後方互換のために、今も使えるとのこと3。 escaped echo
という名前で定義されている。