LaravelのBladeで{{ csrf_field() }}が通る理由
なにかの間違えで、{{ csrf_field() }}
と書いていたんだけどそれが通るということがあって、{{ ... }}
の仕様どうなってるのかなと思い確認した。
{{ ... }}
は紆余曲折を経て、<?php echo e(...) ?>
に変換されていた。e()
はPHPerなら予想がつくだろう通りhtmlspecialchars()
のラッパで、かつHtmlable
インターフェイスを実装したクラスのインスタンスはエスケープせずに通す、という仕様だった。まあ、よくあるやつだ。
なお調査ルートは以下の通り。
{{
,}}
は$BladeCompiler::contentTags
で定義されている。$BladeCompiler::contentTags
はCompilesEchos::compileRegularEchos()
で使用されている。compileRegularEchos()
で{{ ... }}
から<?php echo e(...) ?>
への変換が実装されている。- ただし1, 2の関係が多少ややこしくて、
$BladeCompiler::compilers
でBladeCompiler::parseToken()
経由でCompilesEchos::compileEchos()
が呼び出されて、そこからさらにcompileRegularEchos()
が、という流れになっている。
CompilesEchos
などはBladeCompiler
から5.4で分割されたようだが、もうちょっとリファクタリングして欲しい感じある。
あと、Htmlable
周りはドキュメントに書かれていないが、これを知らないことにより{{ ... }}
の使い方を間違えた場合に(多分)XSSしうるので、ドキュメントにちゃんと書いて欲しいなあ。{{ ... }}
は常にエスケープするわけではないあたりを。
エスケープしない可能性があるのはHtmlable
に関してだけではない。調査の中でBlade::setEchoFormat()
という危ないメソッドの存在も知った。これを適当に書き変えちゃうと、変換先がe(...)
に限らなくなってしまうので、最悪エスケープをなかったことにできたりもして危ない。まあ普通書き換えないだろうけど、もうちょっとなんとかならなかったのか。
また関連して{{{ ... }}}
というのを見つけた。元々はこれと、{!! ... !!}
だけだったのが、5から{{ ... }}
が追加されたようだ。setEchoFormat()
は見ないので安定しているかと思いきやe()
を使ってるのでHtmlable
は通すし、そもそもドキュメントからも消えているので多分使わない方がいい。
なお当初の疑問であった{{ csrf_field() }}
についてだが、とりあえずいろいろ考えると{!! csrf_field() !!}
などするのではなく{{ csrf_field() }}
にするのが妥当そうである。が、そもそも5.6以降は@csrf
, @method
なんてのができており、こちらを使うのがより妥当そうである。