symfony のエスケープ処理

森川です。

今回は symfony のエスケープ処理について、きちんと解説したいと思います。

まず、symfony のデフォルトでは変数を出力するときのエスケープが無効になっています。

つまりデフォルトの状態だとユーザが入力した変数を出力している場合、危険性があるかもしれないということです。


<?php echo $sf_params->get('name') ?>
<?php echo form_tag('test') ?>
<?php echo input_tag('name') ?>
<?php echo submit_tag('テスト') ?>
</form>

上記のスクリプトでは、$sf_params->get('name') が脆弱性アリな状態となっています。

これは非常にまずいので、エスケープを有効にしてあげましょう。そのためには、apps/[name]/config/settings.yml を編集します。


all:
  .settings:
    escaping_strategy: both
    escaping_method: ESC_ENTITIES

21行目、41行目、55,6行目をコメントアウトして(symfony 1.0.13の場合)、さらに55行目の bc を both にします。

こうしてあげると、スクリプト自体を変更しなくてもきちんとエスケープされるようになります。

とりあえずめでたしめでたし、と言いたいところなのですが、今回はここからが本題です。

エスケープされたとはいえ、なぜエスケープされるのか、といったことがいまいちよくわかりませんし、escaping_strategy,methodの意味もいまいちよくわかりません。ということで、それぞれの意味を簡単に説明したいと思います。

まずは、escaping_strategy についてです。この値はどの変数をエスケープするのか、ということを設定します。

設定値として取り得る値は bc both on off の4種類がありますが、それぞれ違いがあります。

違いを説明する前に、テンプレート内で使用できる変数を2種類に分けます。一つは$var_name のような形式で、もう一つは $sf_data->get('var_name') の形式です。4種類での違いは以下のようになります。

・bc: $var_name はエスケープされない、$sf_data->get('var_name') はエスケープされる
・both: 両方ともエスケープされる
・on: $var_name は使用できない、$sf_data->get('var_name')はエスケープされる
・off: $var_name はエスケープされない、$sf_data は使用できない

ちなみに、both、off以外を使用することはまずないと個人的には思っています。なので、あんまりこの説明も意味がないかもしれません(まぁ簡単なTIPSだと思って下さい)。。。もっと深く知りたい方は symfony の view/sfPHPView.php を見るとよいと思います。

次の escaping_method ですが、こちらはエスケープの方法を指定します。

取り得る値は ESC_RAW、ESC_ENTITIES、ESC_JS、ESC_JS_NO_ENTITIES の4種類です。それぞれの違いは以下のようになります。

・ESC_RAW:何もエスケープしません。
・ESC_ENTITIES: htmlentities関数を使用してエスケープします。
・ESC_JS: htmlentities関数でエスケープした後に、addcslashesでエスケープされます。
・ESC_JS_NO_ENTITIES: addcslashesでのみエスケープされます。

addcslashes では JavaScript で使用するときにエスケープする必要がある文字をエスケープします。

ちなみに、MY_ESC と escaping_method として指定する場合は以下のようなヘルパーを用意します。


<?php
function my_escaping_func($value)
{
  // 必要な処理を $value に加えて return する
}
define('MY_ESC')

symfony本体のhelper/EscapingHelper.php を見ればよくわかると思います。

ここからはTIPSになりますが、ESC_ENTITIESでエスケープする場合、オブジェクトや配列の取り扱いが少々ややこしくなるので、注意が必要です。

どういうことかというと、テンプレート内で使用するすべての変数がsymfony本体の view/escaper/sfOutputEscaper*.class.php で定義されているオブジェクトに変わってしまうのです。

一番困るのが、先ほどのスクリプトでもあった、getメソッドです。たとえば、以下のソースを見て下さい。


<?php echo $sf_params->get('name', '空ですよ') ?>

このソースでは何も指定されていなければ、「空ですよ」という文字列が出てくることを期待しますが、これが出てこないのです。

なぜかといえば、sfOutputEscaperObject.class.php の get 関数が呼ばれてしまい、 sf_params(sfParameterHolder)のget関数は呼ばれないのです。

たとえば、以下のように書き換えることができます。


<?php echo $sf_request->getParameter('name', '空ですよ') ?>
<?php echo $sf_data->getRaw('sf_params')->get('name', '空ですよ') ?>

このように get 関数を使用する場合は注意してください。それと同様に in_array などの関数を使用すると、配列型ではない旨のエラーが出たりしてしまいます。

エスケープ専用のオブジェクトになっている、ということを念頭に入れておけば、問題が起きてもすぐに解決できるようになると思います。