FuelPHP 1.6.1 以下へのセキュリティ勧告(脆弱性)

セキュリティ勧告ページ

FuelPHP 本家サイトにセキュリティ勧告(Security Advisories)のページができました。

公開された 2件の脆弱性

そして、1.6.1 以下に影響する脆弱性 2件の情報が公開されました。いずれも、リスクは低とされています。

  • DB quote_identifier() の SQL インジェクションの可能性
  • xss_clean() の Unicode EM Space の処理の問題による XSS の可能性

いずれの報告も、三井物産セキュアディレクションの寺田さんによるものです。


DB quote_identifier() の問題は、1.7/develop で修正済みであり、1.6.1 以前には以下のパッチを当てることで修正できます。

EM Space の問題は、エスケープせずに出力した場合、いくつかのブラウザで問題になるようです。これは、FuelPHP が使用している htmlLawed というライブラリの問題です。

セキュリティ問題の報告先

また、セキュリティ上の問題についての報告は、コンタクトフォームからするように明記されました。

脆弱性情報がいきなり公開されてしまうと、開発元が修正版を用意することができず、ユーザが脆弱性に対応することできない状態で、公開された情報に基づき攻撃されるリスクが高まります。

脆弱性を発見した場合は、いきなり自分のブログで公開したり、公開されている ML やフォーラムに書き込んだりせず、開発者の指定した報告先や、あるいは IPA へ報告するようにしましょう。

enchantMOON を 2.4.1 にアップデートした

昨日、バージョンアップしたばかりですが、朝起きたら 2.4.1 がリリースされていたようです。

なので、今日もアップデートしました。


ダウンロード中。


何故か 16% ダウンロードしたところで、リブートされるようです。

そういえば、昨日のアップデートでは 70数% でリブートしたように思います。


バージョンの確認。

今日は 3分くらいでアップデートが完了しました。

enchantMOON を 2.4.0 にアップデートした

enchantMOON がバージョンアップして 2.4.0 がリリースされたようなので、アップデートしてみました。

「update」コマンドを実行して、電源ケーブルを繋いで、あとは待つだけでした。「数時間掛かる」みたいなメッセージが出ましたが、30分も掛からず完了しました。

アップデート完了すると、Web 経由で「シール」をダウンロードできました。以下がダウンロードしたシール。


バージョンの確認。

FuelPHP 入門ハンズオン vol.2 を開催しました

昨日、FuelPHP 入門ハンズオン vol.2 を、名古屋で開催しました。

このハンズオンは PHP に関する基本的な知識がある方を対象に、FuelPHP のインストールから使い始めることができるようになるまでを目標とするものです。 今回、2回目ですが、東は浜松から、西は奈良からと遠方より参加される方もいらっしゃいました。

前回より 1時間ほど時間の余裕がありましたが、内容的には前回と同一の範囲をカバーするものになりました。詳細に興味のある方は、以下のスライドをご覧ください。

Git & GitHub を使ったことがない人が多く、また、今回は、Windows マシン(GitHub for Windows の動作がおかしいなど)でのトラブルと XAMPP for Mac でのトラブル(MySQL データベースに接続できない)が発生し、そこで時間を食いました。トラブルの発生をおさえることと、トラブル発生時の対応をどうするかが、今後の課題だと思います。

それと、FuelPHP 以前に Git のハンズオンが必要なような気がします。

FuelPHP 1.6.1 で SQLite を使い Scafold する

SQLite でも Scafold したコードが動きましたので、手順を書いておきます。

あまりきちんと検証していないので、SQLite の設定など改善すべき点がありましたら、お知らせ頂けるとありがたいです。


config.php で orm を always_load するようにします。

データベース設定を SQLite にします。

--- a/fuel/app/config/development/db.php
+++ b/fuel/app/config/development/db.php
@@ -5,10 +5,15 @@
 
 return array(
    'default' => array(
-       'connection'  => array(
-           'dsn'        => 'mysql:host=localhost;dbname=fuel_dev',
-           'username'   => 'root',
-           'password'   => 'root',
+     'connection' => array(
+         'dsn' => 'sqlite:' . APPPATH . 'tmp/sample.sqlite3',
+         'persistent' => false,
+         'compress' => false,
        ),
+     'identifier' => '"',
+     'table_prefix' => '',
+     'enable_cache' => true,
+     'profiling' => true,
+     'charset' => '',
    ),
 );

oil コマンドで scaffold します。

$ php oil generate scaffold bbs post_date:date message:varchar[100] --singular

migration ファイルがそのままでは動かないので修正します。

--- a/fuel/app/migrations/001_create_bbs.php
+++ b/fuel/app/migrations/001_create_bbs.php
@@ -7,13 +7,12 @@ class Create_bbs
    public function up()
    {
        \DBUtil::create_table('bbs', array(
-           'id' => array('constraint' => 11, 'type' => 'int', 'auto_increment' => true, 'unsigned' => true),
-           'post_date' => array('type' => 'date'),
-           'message' => array('constraint' => 100, 'type' => 'varchar'),
-           'created_at' => array('constraint' => 11, 'type' => 'int', 'null' => true),
-           'updated_at' => array('constraint' => 11, 'type' => 'int', 'null' => true),
-
-       ), array('id'));
+         'id' => array('type' => 'integer primary key', 'autoincrement' => true),
+         'post_date' => array('type' => 'text'),
+         'message' => array('type' => 'text'),
+         'created_at' => array('type' => 'integer', 'null' => true),
+         'updated_at' => array('type' => 'integer', 'null' => true),
+     ));
    }
 
    public function down()

oil コマンドで migrate します。

$ php oil refine migrate

views/bbs/index.php がバグっているので修正します。これは、--singular オプションを付けたときに発生するバグです。

--- a/fuel/app/views/bbs/index.php
+++ b/fuel/app/views/bbs/index.php
@@ -10,14 +10,14 @@
        </tr>
    </thead>
    <tbody>
-<?php foreach ($bbs as $bbs): ?>       <tr>
+<?php foreach ($bbs as $bb): ?>      <tr>
 
-           <td><?php echo $bbs->post_date; ?></td>
-           <td><?php echo $bbs->message; ?></td>
+         <td><?php echo $bb->post_date; ?></td>
+         <td><?php echo $bb->message; ?></td>
            <td>
-               <?php echo Html::anchor('bbs/view/'.$bbs->id, '<i class="icon-eye-open" title="View"></i>'); ?> |
-               <?php echo Html::anchor('bbs/edit/'.$bbs->id, '<i class="icon-wrench" title="Edit"></i>'); ?> |
-               <?php echo Html::anchor('bbs/delete/'.$bbs->id, '<i class="icon-trash" title="Delete"></i>', array('onclick' => "return confirm('Are you sure?')")); ?>
+             <?php echo Html::anchor('bbs/view/'.$bb->id, '<i class="icon-eye-open" title="View"></i>'); ?> |
+             <?php echo Html::anchor('bbs/edit/'.$bb->id, '<i class="icon-wrench" title="Edit"></i>'); ?> |
+             <?php echo Html::anchor('bbs/delete/'.$bb->id, '<i class="icon-trash" title="Delete"></i>', array('onclick' => "return confirm('Are you sure?')")); ?>
 
            </td>
        </tr>

Linux で Internet Explorer 6,7,8,9,10 での表示を確認する

http://www.modern.ie/ja/virtualization-tools から Linux 用の仮想マシンVirtualBox)をダウンロードします。

IE9.Win7.For.LinuxVirtualBox の場合は、以下のようにします。

$ wget -c i https://az412801.vo.msecnd.net/vhd/IEKitV1_Final/VirtualBox/Linux/IE9_Win7/IE9.Win7.For.LinuxVirtualBox_2.txt

ダウンロードが完了したら、解凍します。

$ chmod +x IE9.Win7.For.LinuxVirtualBox.part1.sfx
$ ./IE9.Win7.For.LinuxVirtualBox.part1.sfx

IE9 - Win7.ova」が作成されました。これを VirtualBox にインポートします。


オーバーロードされたプロパティと emtpy() 〜 nagoya.php vol.2 での話

http://blog.ohgaki.net/php-5-4-accessor-php にある trait を使ったアクセサのサンプルコード(微妙にいじってますが、ロジックは全く同じです)。

<?php
// Example code how to eliminate getter and setter methods
// with traits introduced from PHP 5.4

trait Accessors
{
  public function __get($name)
  {
    if ($this->r_property[$name]) {
      return $this->$name;
    } else {
      trigger_error("Access to read protected property");
    }
  }

  public function __set($name, $value)
  {
    if ($this->w_property[$name]) {
      $this->$name = $value;
    } else {
      trigger_error("Access to write protected property");
    }
  }
}

class OrderLine
{
  use Accessors;

  private $r_property = array('price' => 1, 'amount' => 1);
  private $w_property = array('price' => 0, 'amount' => 1);

  protected $price = 100;
  private $amount;
  private $tax = 1.15; // property without getter/setter

  public function getTotal()
  {
    return $this->price * $this->amount * $this->tax;
  }
}

$line = new OrderLine;

//$line->price = 20;  // Notice error
$line->amount = 3;

echo "Total cost: ".$line->getTotal(), PHP_EOL;

で、上記のようなコードで、オーバーロードされたプロパティを emtpy() したら true が返ってハマったというのが、nagoya.php での話でした。

var_dump(empty($line->amount));  // true

これは、PHP の仕様です。

PHP マニュアルの「注意」

ここで、以下のような注意が PHP にマニュアルにありました。

注意:

オーバーロードされたプロパティを、 isset() 以外の言語構造の中で使うことはできません。 つまり、オーバーロードされたプロパティに対して empty() がコールされたとしても、オーバーロードされたメソッドはコールされないということです。

この制約を回避するには、オーバーロードされたプロパティを そのスコープのローカル変数にコピーしてから empty() に渡さなければなりません。
http://www.php.net/manual/ja/language.oop5.overloading.php

これ、何なんでしょう?

オーバーロードされたプロパティを、 isset() 以外の言語構造の中で使うことはできません」→ isset() なら使えるの?使えないですよね。

var_dump(isset($line->amount));  // false

empty() と isset() どちらも同じようにオーバーロードされたプロパティでは機能しません。

ということで、この「注意」はちょっとよくわかりません。ただの間違い?

あと、「この制約を回避するには、オーバーロードされたプロパティを そのスコープのローカル変数にコピーしてから empty() に渡さなければなりません」→ つまり、empty() 使いたかったら、こうしろと。

$amount = $line->amount;
var_dump(empty($amount));  // false

しかし、そんなことを強制するのは、正直、無理っぽいですよね。

どうすればいいのか?

__isset() を定義すればいいと思うよ。

<?php
// Example code how to eliminate getter and setter methods
// with traits introduced from PHP 5.4

trait Accessors
{
  public function __get($name)
  {
    echo '__get():';
    if ($this->r_property[$name]) {
      return $this->$name;
    } else {
      trigger_error("Access to read protected property");
    }
  }

  public function __set($name, $value)
  {
    echo '__set():';
    if ($this->w_property[$name]) {
      $this->$name = $value;
    } else { 
      trigger_error("Access to write protected property");
    }
  }

  public function __isset($name)  // これです
  {
    echo '__isset():';
    if ($this->r_property[$name] === 1) {
      return true;
    } else {
      return false;
    }
  }
}

class OrderLine
{
  use Accessors;

  private $r_property = array('price' => 1, 'amount' => 1);
  private $w_property = array('price' => 0, 'amount' => 1);

  protected $price = 100;
  private $amount;
  private $tax = 1.15; // property without getter/setter

  public function getTotal() {
    return $this->price * $this->amount * $this->tax;
  }
}

$line = new OrderLine;

//$line->price = 20;  // Notice error
$line->amount = 3;

echo "Total cost: ".$line->getTotal(), PHP_EOL;

var_dump(isset($line->amount));
var_dump(empty($line->amount));

そうすると、結果は、以下のようになります。

__set():Total cost: 345
__isset():bool(true)
__isset():__get():bool(false)

ちなみに、この話、trait は関係ありません。