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 などの関数を使用すると、配列型ではない旨のエラーが出たりしてしまいます。
エスケープ専用のオブジェクトになっている、ということを念頭に入れておけば、問題が起きてもすぐに解決できるようになると思います。