FuelPHPのwhitelisted_classesにクラスを追加する人はどういう影響があるかきちんと把握しましょう

FuelPHPのデフォルトのビューはPHPであり、FuelPHPはデフォルトでビューに渡した値をHTMLエスケープします。

文字列をビューに渡す場合はそれで問題ないですが、オブジェクトをビューに渡すと少々話がややこしくなります。

例えば、以下のようなクラスを渡す場合です。

class Data
{
    private $var;

    public function get_var()
    {
        return $this->var;
    }

    public function set_var($var)
    {
        $this->var = $var;
    }
}

例えば、こんなコントローラで。

class Controller_Test extends Controller
{
    public function action_index()
    {
        $obj = new Data();
        $obj->set_var('<s>abc</s>');
        $data['obj'] = $obj;
        return View::forge('test/view', $data);
    }
}

ビューも単純に以下のようにします。

<?php

echo $obj->get_var();

この場合、FuelPHPは以下のエラーを返します。

RuntimeException [ Error ]: Object class "Data" could not be converted to string or sanitized as ArrayAccess. Whitelist it in security.whitelisted_classes in app/config/config.php to allow it to be passed unchecked.

そこで、なんだかよくわからずに、app/config/config.phpのwhitelisted_classesに以下のように'Data'を追加するとエラーは出なくなります。

        'whitelisted_classes' => array(
            'Fuel\\Core\\Response',
            'Fuel\\Core\\View',
            'Fuel\\Core\\ViewModel',
            'Closure', 'Data'
        ),

ところが、ブラウザには「abc」と線が引かれたabcという文字が表示されています。

そうです。タグがエスケープされず使えているということです。もし、ユーザの入力したデータを返すオブジェクトをビューに渡し、whitelisted_classesに追加すれば、XSSの可能性があるということです。

whitelisted_classesはもともと出力時の自動エスケープを無効にするクラスを指定する設定なので、当然といえば当然なんですが。

というわけで、whitelisted_classesにクラスを追加する場合は、そのオブジェクトのデータをビューで表示したときにエスケープ漏れがないことを自分で確認する必要があります。

Sanitizationインターフェイス

FuelPHP 1.7.1からSanitizationインターフェイスが追加されました。これは、ビューでの表示用にデータを加工するためのインターフェイスです。

エスケープ漏れの問題は、ビューに渡されたオブジェクトがデータを表示する際、自分でちゃんとエスケープなり何なりの処理をすれば解消されるわけです。そのためのインターフェイスです。

先ほどのDataクラスにSanitizationインターフェイスを実装すると以下のようになります。

class Data implements Sanitization
{
    private $var;
    private $_sanitization_enabled = false;

    public function get_var()
    {
        if ($this->_sanitization_enabled)
        {
            return Security::htmlentities($this->var);
        }

        return $this->var;
    }

    public function set_var($var)
    {
        $this->var = $var;
    }

    public function sanitize()
    {
        $this->_sanitization_enabled = true;
        return $this;
    }

    public function sanitized()
    {
        return $this->_sanitization_enabled;
    }

    public function unsanitize()
    {
        $this->_sanitization_enabled = false;
        return $this;
    }
}

簡単に説明すると、

  • ビューでの表示時にsanitize()メソッドが呼ばれるので、そこでsanitizeオンの状態にする
  • sanitizeオンの状態での出力はエスケープするように処理する

というものです。

Sanitizationインターフェイスを実装すれば、whitelisted_classesに追加する必要はありません。

これで、ブラウザでは「<s>abc</s>」と表示されました。

ただし、

class Data implements Sanitization
{
    public $var;

のようにpublicでプロパティを宣言し、ビューで以下のように

echo $obj->var;

直接アクセスしてしまえば、エスケープされず生のデータが表示されてしまいますので、Sanitizationインターフェイスを実装していても無意味になります。

これを防ぐには、publicなプロパティをなくし、マジックメソッドでプロパティへのアクセスを完全にコントロールする必要があります。

Date: 2014/02/19

Tags: fuelphp