Home > アーカイブ > 2010-11

2010-11

PHP unserialize()が__destruct()を実行する?

この記事の所要時間: 328

CakePHPのセキュリティホール(まだの方はご対応を!)から、unserialize()が話題になっています。

このセキュリティホールは、外部から送信された値をチェックせずにunserialize()したことが引き金になっており、安全でない値をunserialize()することの危険性が指摘されています。

下記エントリでは、コードを交えてunserialize()から__destruct()が実行される過程が解説されています。

PHP5 __destruct() and unserialize() function – TokuLog 改メ tokuhirom’s blog

念のための補足なのですが、unserialize()から__destruct()が呼ばれるわけではありません。

下記コードは、PHP5.3.3で実行しています。

unserialize()から実行される関数

unserialize()から実行される関数(メソッド)は以下です。

__autoload、spl_autoload*などのオートローダー

デシリアライズするインスタンスのクラス定義を探す時に実行される可能性があります。

unserialize_callback_funcで設定した関数

上記オートローダー実行してもデシリアライズするインスタンスのクラス定義が存在しない場合に実行されます。ini_set()等で設定していなければ実行されません。

__wakeup

デシリアライズしたインスタンスに__wakeupメソッドが定義されていれば実行されます。

unserialize()から間接的に実行される関数

デシリアライズしたインスタンスの操作によって、結果実行される可能性があるのは以下です。

__destruct()

デシリアライズしたインスタンスの参照が0になったタイミングで実行されます。通常必ず実行されます。

__toString() / __set() / __get() / __call() / __invoke()などのマジックメソッド

デシリアライズしたインスタンスへの操作によって実行される可能性があります。

この中で実行されそうなのは__toString()です。unserialize()した値が文字列だと想定して以下のようなコードを書いていると__toString()が実行されます。

func start
Foo::__toString
func end

unserialize()で__destruct()が実行される?

上記でも触れたように__destruct()は、unserialize()で呼ばれるわけではなく、デシリアライズしたインスタンスの参照が0になるタイミングで実行されます。

これは通常のクラスインスタンスと同じ挙動です。

下記コードを実行してみるとその挙動が確認できます。

実行結果

% php unserialize.php
func start
func end
Foo::__destruct <--- "func end"の後

ただ以下のようにunserialize()の戻り値を変数に格納しない場合は、デシリアライズしたインスタンスへの参照がありませんので、即時に__destruct()が実行されます。

func start
Foo::__destruct   // unserialize()して、即__destruct()が呼ばれる
func end

[おまけ] protected/privateな変数もデシリアライズ可能

ちなみにunserialize()では、protected/privateなインスタンス変数もデシリアライズ可能です。インスタンス変数をprotected/privateにしたところで、unserialize()で汚染される可能性があることには変わりありません。

object(Foo)#2 (3) {
  ["var1"]=>
  string(6) "public"
  ["var2":protected]=>
  string(9) "protected"
  ["var3":"Foo":private]=>
  string(7) "private"
}

でも結果としては実行される

unserialize()が__destruct()を直接実行するわけではないですが、デシリアライズしたインスタンスはどこかで破棄されるので、結果として__destruct()が実行されることには変わりありません。

ただやみくもに「unserialize()が__destruct()を実行する」と考えると問題を見誤る可能性があるのでご注意を。

参考リンク

CakePHPのSecurityComponentに深刻なセキュリティホールが見つかりました

この記事の所要時間: 29

すでにご存知の方も多いと思うのですが、CakePHPに深刻なセキュリティホールが見つかりました。

SecurityComponentの実装に問題があり、結果、外部から任意のコードを実行させることができるという深刻な内容です。

セキュリティホールの概要や攻撃手順については以下のエントリが詳しいですので、ご一読を。
CakePHP の PHP コード実行の脆弱性を使って CakePHP を焦がす

なお、今回の問題はSecurityComponentを利用していない場合は発生しません。

もしSecurityComponentを利用している場合は、以下のいずれかの方法で早急に対策してください。

1. CakePHP1.2.9 or 1.3.6にアップグレードする。

この脆弱性を受けて修正バージョンが出ています。

CakePHP 1.3.6 and 1.2.9 released | The Bakery, Everything CakePHP

2. SecurityComponentを自分で修正する

バージョン変更が容易で無い場合は、SecurityComponent を修正します。

以下に修正差分があるので、このコードをSecurityComponentに適用してください。

https://github.com/cakephp/cakephp/commit/e431e86

3. SecurityComponentの利用をやめる

SecurityComponentを CSRF 対策で利用している人も多いと思いますが、現在のSecurityComponent は必要以上に複雑な実装となっていて内容が掴みづらいという印象です。

いっそのこと自分に必要なシンプルなものを自作してしまうのもひとつの方法です。(私も社内で開発したものを使っています。)

まずは確認を

CakePHPを使っている方は、まずはSecurityComponentを利用しているか確認を。

簡易的ですが、SecurityComponent を使っているかどうかは以下のコマンドでも探せます。

% find app/ -name "*.php" | xargs grep "'Security'"
% find plugin/ -name "*.php" | xargs grep "'Security'"

まだ実際のサイトでの攻撃例は聞かないですが、今後発生する可能性があります。お気をつけを。

会社名刺626枚のうち、Twitterアカウントが入っているのは何枚か?

この記事の所要時間: 133

会社の名刺にTwitterアカウント入れてますか?

1×1の名刺をリニューアルしようともろもろ検討中です。

せっかくだからTwitterアカウントを入れようかなと思って、他社がどうしているのかを手元の名刺で調べてみました。手持ちの名刺を全部見ると大変なんで、ぱっと取り出せた626枚の会社名刺で見てます。

ちなみに取り出した名刺の束には個人名刺も150枚ほど混ざってたので、800枚近くありました。夜中に何をやってんだか。。。

626枚の会社(法人)名刺

626枚の会社名刺の中でTwitterアカウントが入っていたのは、9件でした。1%強と思ったより少なかったですね。

会社をIT系とそれ以外に分けると、やはり9枚は全てIT系企業でした。

  • IT系企業:359枚(Twitterアカウント:9枚)
  • 一般企業:267枚(Twitterアカウント:0枚)

9件は全てここ一年以内で頂いた名刺

Twitterアカウントが入っていた会社名刺は全てここ一年以内に頂いた名刺ばかりでした。

名刺の中には数年前に頂いたものもあるので、もしかしたら今現在の名刺にはTwitterアカウントが入っているかもしれませんね。

懐かしい名刺もちらほら

なんだか色々な名刺を見ているとその当時のことがよみがえってきて懐かしいですね。

IT系では転職されている方も多いので、前職の名刺や前々職や前々前職の名刺が出てきたり:D

また、同じ会社でも現在とは名刺が変わったりして、時の流れを感じます。

1×1の新名刺にTwitterのアカウントを入れると、数年経ってTwitterが廃れると意味ないなーと思いつつ、どうせいずれ名刺は変えるものなので、時代を反映するものとして入れて見ようと思います。

新名刺は、乞うご期待。

Google Docs のスプレッドシートに表データをコピペする

この記事の所要時間: 211

Google Docs のスプレッドシートに表データをコピペする方法です。

Google Docs 便利ですね。Excel に比べると多少操作性や機能は劣りますが、どこからでも共有できるというのはそれを補って余りあります。

そんな便利なスプレッドシートなのですが、コピーしたCSVを表形式でスプレッドシートに貼る機能がありません。

もちろんCSVをファイルに書き出してインポートすれば良いのですが、今画面上に表示しているCSVをそのままコピペしたい時ってありませんか。

例えば、以下のようなCSVをコピペしてスプレッドシートに貼り付けると、1行が1つのセルに文字列として格納されてしまいます。

1,原田哲也,TZM250
2,加藤大治郎,NSR250
3,青山博一,RS250RW

タブ区切りならいける

何とかならないかなーと試してみると、なぜかタブ区切りだと各セルに値を入れることができました!

先のCSVをタブ区切り(TSV)に変更して、スプレッドシートに貼り付けると、

1    原田哲也    TZM250
2    加藤大治郎 NSR250
3    青山博一    RS250RW

ばっちりいけます。

ただし貼り付けはショートカットキーで

これ不思議なのですが、マウスの右クリックから「貼り付け」を選択するとセルに値が入りません。

ショートカットキーの command+v(Windowsなら、ctrl+v)でペーストすれば ok です。

psqlの検索結果をスプレッドシートに貼り付け

そもそもなんでこれをやりたかったかというと、psqlで検索した結果をスプレッドシートに貼り付けたかったから。

psqlで検索結果をTSVにするのは以下。

$ psql db_name
db_name=> \a   // クエリ結果の整形オフ
db_name=> \t    // 検索結果のみ出力
db_name=> \pset fieldsep '\t'  // 列の区切り文字をタブに(カンマにすれば CSV に)

db_name=> select id,name,code from users order by id; // 検索クエリ
1     原田哲也     TZM250
2     加藤大治郎     NSR250
3     青山博一     RS250RW

あとはこの結果をコピーして、スプレッドシートに貼り付けるだけ。あー便利。

PHPで認証して、mod_xsendfileでファイルを出力する

この記事の所要時間: 545

ApacheでX-Sendfileが利用できるmod_xsendfileをPHPと連携して使ってみました。

PHPで認証してから、許可したユーザのみにファイルを出力する、という処理を実装する場合、ファイルはdocument_root外に配置しておいて、readfile()やfpassthru()でファイルを出力するという手法を良く使います。

この方法でも問題無い場合が多いのですが、容量の大きいファイルを出力する際は思ったようなスピードが出ない時があります。

そのような時はmod_xsendfileを使って、ファイル出力の部分をApacheに任せてしまう方法が有効です。

ここでは2010/11/12時点の最新版であるmod_xsendfile 0.12を対象としています。またインストール環境はRHEL、CentOSを想定しています。

mod_xsendfileのインストール

mod_xsendfileはApache2/2.2で動作するモジュールです。

インストールにあたって、apxsコマンドが必要となるので、もしインストールしていない場合はyum等でインストールします。(apxsは、RPMならhttpd-develパッケージに含まれています)

% sudo yum install httpd-devel

次にmod_xsendfileをダウンロードします。mod_xsendfileは、mod_xsendfile.cという1ファイルだけなので、それをダウンロードしてインストールします。

ブラウザからダウンロードする際は問題無いのですが、wgetでダウンロードしようとすると証明書エラーが発生します。–no-check-certificateオプションを付けるとこのエラーを避けることができます。

% wget --no-check-certificate https://tn123.org/mod_xsendfile/mod_xsendfile.c

apxsでmod_xsendfileをインストールします。

% sudo /usr/sbin/apxs -cia mod_xsendfile.c

インストールと共にhttpd.confにLoadModuleが追加されます。

[/etc/httpd/conf/httpd.conf]

LoadModule xsendfile_module   /usr/lib/httpd/modules/mod_xsendfile.so

mod_xsendfileを設定する

mod_xsendfileを利用するために設定を行います。

httpd.confでXSendFilePathを指定する

まず、XSendFilePathで出力を許可するパスを指定します。mod_xsenfileではdocument_root外のファイルを出力することができるので、誤って意図しないファイルの出力を避けるために出力対象のパスを指定する必要があります。

例えば、出力するファイルは「/path/to/images」以下にあるなら以下のように指定します。

[/etc/httpd/conf/httpd.conf]

XSendFilePath /path/to/images

XSendFilePathは、VirtualHostやDirecortyディレクティブでも指定が可能です。Directoryディレクティブでは実行するPHPファイルのパスを指定できます。例えば、CakePHPで指定するなら以下のようになります。(出力ファイルがapp/data以下にあるとします。)

[/etc/httpd/conf/httpd.conf]


  XSendFilePath /path/to/app/data

XSendFileをonにする

次にXSendFileをonに設定します。これによりX-Sendfileが有効となります。

/etc/httpd/conf*な設定ファイルでも記載できるのですが、アプリ側ファイルの方が都合が良いので、.htaccessに設定を書いておきます。

[/path/to/app/webroot/.htaccess]


    XSendFile on

これで設定は完了です。あとはPHPから出力するファイルを指定します。

XSendFileAllowAboveは削除

なおmod_xsendfile0.9ではXSendFileAllowAboveという設定項目があったのですが、これは0.10で削除されたようです。記載しているとエラーになるのでご注意を。

PHPから出力するファイルを指定

mod_xsendfileと使って、PHPからファイルを出力するにはX-Sendfileヘッダで出力ファイルパスを指定します。

下記のようにreadfile()を記載していた箇所(下から2行目)を、X-Sendfileを出力するheader()に書き換えます。

これでmod_xsendfileからファイルが出力されます。

ちょっと気になったこと

X-Sendfileヘッダは外部に出力される?

X-Sendfileヘッダには出力ファイルのパスを指定するので、外部に出力されるのはあまり好ましいことではありません。mod_xsendfileの処理が有効になっていれば、このヘッダは除去され外部には出力されないようになっています。

0バイトのファイルが出力される

X-Sendfileヘッダで指定したファイルパスが、XSendFilePathで指定したパスにマッチしないと0バイトになるようです。必要であれば、XSendFilePathにファイルパスを追加しましょう。

404が発生する

X-Sendfileヘッダで指定したファイルが存在しない場合は404になります。

PHP以外でも使える?

X-Sendfileヘッダをレスポンスヘッダに出力できれば良いので、PHP以外でも利用できるようです。

Apacheで使えるX-Sendfile

数年前にこんなエントリも書いたりしていたのですが、たしか当時はApacheでの実装は無かったと思います。

なんとなくそのままのイメージでいたので、Lighttpd(nginxにも似た機能があったような。X-Accel-Redirectかな。)いいなあと思ってたら、実は結構前からApacheでもできるようになったんですね。

この認証ロジックはPHP、出力はmod_xsendfileと、それぞれ得意な分野に分けるのは、Unixっぽくて好きです。

GoogleAppEngine に関する議論について思うこと

この記事の所要時間: 210

appengine関連でTogetterから議論が巻き起こってますね。最近すっかりappengineをご無沙汰だった自分としては、一連のtweetやblogを読んだおかげですっかり熱が戻ってきました。

議論の発端になったのは以下のTogetter。

Togetter – 「Google AppEngineについて思うところ」

議論を見ていて感じるところがあったので、ざざっと。

  • @makotokuwata さんの主張自体は自分はそれほど違和感無かった。ただ表現がキツイ箇所があったのが琴線に触れてしまったのかな。
  • 議論から有用なtweetやblogエントリが生まれてきた。これはとても参考になった。
  • 意識的にそうしていたわけではないだろうけど、寄ってたかって反論していくのは見ていて @makotokuwata さんが気の毒に感じた。
  • @makotokuwata さんがこれまでされてきた活動(Python4PHPer)は素晴らしいと思う(自分もイベントは気になってた)し、これからも続けられると良いのでは。(
    大きなお世話かもしれませんが)
  • ちなみに反論していた人には「appengine ja night」な方も多かったけど、自分がイベントに参加した時はアットホームな雰囲気でとても楽しかったです。技術的な内容ゼロの自分のLTも笑顔で聞いてくれたし。別にこわいひとたちでは無い(と思う)。
  • ようは、表現が少し過激だったり、ちょっとした言葉の行き違いが原因かと。自分も感情的になるとついつい余計なことを書いてしまうから気をつけないと。
  • 多分、飲み屋で同じ話すれば「おまえなにいうてんねん」というノリで軽い議論になるだけで、こんなややこしい話にはならなかったでしょうね。オンラインのコミュニケーションは難しい。。。
  • どちらかがどちらかのイベントに参加して懇親会とかで話せば、わだかりも解けると思うんだけどなー。

今回のことでこれまで頑張ってこられた @makotokuwata さんが辛い思いをされているのは、とても悲しいことなので、気にせずに頑張って下さい。

そういえば appengineを追いかけてる時に作ろうと思って放置していたものとかあるから、またやりだそうかな。

appengine について参考になったエントリ

Home > アーカイブ > 2010-11

検索
フィード
メタ情報

Return to page top