MovableTypeの最近のブログ記事

こんな連載が始まっちゃったので僕もひとつ(忙しくて中々追いつかんのですが)。

検索エンジンからの流入時に検索ワードを拾ってサイト内検索をリアルタイムに実行する例なんかも面白いのですが、単純に流入元のドメインによって分岐するなんて例も面白いんじゃないかと。Twitterからの流入だったら「フォローしてね」って表示させてみたりFacebookからの流入時に「いいね!」ボタンをわかりやすく配置したり外部サイトからの流入時にサイトの利用規約を表示するとか、まーアイデア次第で。

<MTIfTheHTTPReferrer domain="www.google.com">
www.google.comからの流入
</MTIfTheHTTPReferrer>

<MTIfTheHTTPReferrer domain="google.com" scope="backward">
*google.comからの流入
</MTIfTheHTTPReferrer>

<MTIfTheHTTPReferrer domain="www.google" scope="foward">
www.google* からの流入(www.google.com や www.google.co.jp)
</MTIfTheHTTPReferrer>

<MTIfTheHTTPReferrer domain="google" scope="contains">
googleを含むドメインからの流入
</MTIfTheHTTPReferrer>

<MTIfTheHTTPReferrer scope="empty">
リファラが空の場合
</MTIfTheHTTPReferrer>

プラグインのダウンロード

藤本さんが書かれていたのでDynamicMTMLに絞って初期化の軽量化について書きます。

真新しいMacBook Air(13インチモデル 1.8GHz Intel Core i7 メモリ4GB 1333 MHz DDR3)上のMAMP2.0.5、PHP Version 5.3.6+MySQL5.5.9で計測しました。

計測はDynamicMTML、PowerCMSを突っ込んだ状態でSmart Blogテーマのトプページで計測します。まずは素の状態で計測(単なるPHPファイル)。

...数字に差がない...ときどき0.0001くらいの数値が出ます。ということで仮に0.0001秒としましょう(速いよMBA)。

前提条件

さてここからいよいよ計測していくわけですが、まず、DynamicMTMLはファイルキャッシュのしくみを持っていて、キャッシュが使える状況であればそれを使えということになります(管理画面でチェックボックスにチェック入れるだけです)。今回は当然ですが、それが使えない場合の軽量化、がテーマです。尚、条件付きGETの有効チェックボックスは必要に応じて入れると良いと思います。

条件付きGETってなに?という方はこちらを。

DynamicMTMLの初期化の流れ

DynamicMTML及びMTのダイナミックの初期化の流れを以下におおまかに書きます。

  • mt-config.cgiの設定値などを読み込む
  • ダイナミックパブリッシングのライブラリ(mt/php以下のファイル群)、プラグイン、addons以下のphpライブラリをスキャンする。
  • この時、グローバルモディファイア等を登録する
  • 各プラグインのphpディレクトリ以下の init.foo.php があればこれを実行する。
  • DB接続して、設定値等を読み込み、ブログをロードする
  • ファイルパスからmt_fileinfoレコードを取得して、どのアーカイブかを判断、エントリー等をロードしてコンテクストをセットする
  • テンプレートをビルドする

正確には上記の処理の各所でコールバックが呼ばれ、コールバックプラグインが登録されていればそいつが処理されます。

さて、「ダイナミックパブリッシングのライブラリ(mt/php以下のファイル群)、プラグイン、addons以下のphpライブラリをスキャンする。」ってさりげなく書きましたけど、mt_dir_以下のphp/libディレクト以下のphpファイルいくつありますか? 500以上ありますね。PowerCMSやプラグインやDynamicMTMLも...あわせると1000超えますよね。つまり、初期化の際にこれらすべてスキャンして、init.foo.phpがないか、modifier.foo.phpがないか調べてます。実際は微々たる時間ですけどね。ただ、この辺はeAcceleratorなんかを使うことで多少は改善されます。

また、init.foo.phpの件については、例えば Commercial.pack/php/init.CustomFields.php は何をしているかというと、DBに登録されているカスタムフィールドをロードして、タグ名をsmartyに動的に登録しています(つまりSQLのクエリが投げられてんですね)。カスタムフィールドのタグからの出力をダイナミックに行わない場合、こいつは要らないことになります。他にMultiBlog/php/init.MultiBlog.phpなんかもタグ登録系です。

また「ブログのロード」「mt_fileinfoレコードを取得」ってありますが、例えば携帯、スマホ分岐のみにDynamicMTMLを使っている場合、DB要らないですよね。これ、実はDB接続なしでDynamicMTMLを実行する方法があります。ドキュメントルート以下に生成されている .mtview.phpの先頭付近

    $blog_id      = 1; // 1はブログ/ウェブサイトのID

こいつをコメントアウトすると、DBの初期化をスキップして処理を続行するんですね。

ここら辺から、以下の施策によって初期化の軽量化を図ることにします。

  • eAcceleratorやXcacheを有効にする
  • プラグインの数を減らす
  • DB接続しない

以下、DynamicMTML利用時の各計測結果(10回計測した平均値です)。実際の効果についてはサーバーやDBの状態、運用しているサイトによって違うと思いますし、必要に応じてこういうアプローチがとれる、という程度の参考にしてください。

デフォルトの状態

    0.001767516

※気になるってほどの時間じゃないですけどね...続けます。

Xcacheを有効にした状態で計測

    0.001712704

Xcacheの代わりにeAccelerator(以下、eAccelerator有効で継続)

    0.001612711

標準以外のプラグインなし

    0.000941825

各プラグインディレクトリ以下のphp/init.foo.phpのみ削除

    0.000869989

プラグイン(addon)なし

    0.000636888

blog_id指定なし

    0.000617242

DynamicMTMLは一言でいえば「静的なHTMLファイルの中のMTタグをダイナミックに処理する」ということになるでしょう(HTMLじゃなくてもXMLでもJavaScriptでも何でもいいわけですが)。確かにそのことによってスマートフォンに対する分岐なんかがMTタグで書けるようになったわけです。

ただ、それはDynamicMTMLの一面にすぎません。ただ、一度に紹介しきれないので何度かに分けてご紹介しようと思います。

MTPluginクラスとconfig.php

そうなんです。元々MTのダイナミックパブリッシングはSmartyの拡張なので、Smartyの作法に従って function.mthellomtworld.php みたいなファイルをタグの数だけ用意する必要があります。MTのphp/libディレクトリの下を覗いてみてください。いくつファイルありますか? 正直やってらんねーな、というほどのファイル数です(よくメンテしてるな、てか、よく書いたなこれ)。

DynamicMTMLにはMTPluginというクラスがあって、そいつを継承したクラスを作ることでこんな書き方ができるようになります。ファイルの位置は plugins/HelloWorld/php/config.php です。配列の形で指定するから、ひとつのファイルにまとめてタグを実装できます。案件で使う処理をまとめて書けるからメンテンスもしやすいですよね。

<?php
class HelloWorld extends MTPlugin {
    var $app;
    var $registry = array(
        'name' => 'HelloWorld',
        'tags' => array(
            'function'
                => array( 'helloworld'
                    => 'hdlr_helloworld' ),
        ),
    );
    function hdlr_helloworld($args, &$ctx) {
        return 'Hello MT World.';
    }
}
?>

MT使いの方ならこのコード、ピンとくると思うんですが、そう、Perlでの書き方と殆ど同じですよね。Perlのinit_registryと同じ指定方法です。

この例はテンプレートタグの書き方ですが「Perlと同じ書き方」ができるようにDynamicMTMLにはMT::Objectと'ほぼ'互換のORマッパやMTのタスク(run-periodic-tasksとほぼ同等のことがPHPでかけたり)や、静的ファイルの再構築をPHPで行う方法なんかも含まれています。

次回以降改めてご紹介しますが、僕にしては珍しく? ドキュメントも丁寧に書いているので、宜しければ参考にしてください。

先週の金曜日Six Apart Day 2012に参加してパートナーセッションをしてきました。翌日はMTユーザーの集まりであるMT Cafe Tokyoに参加してコードを書いて発表(コード書いてたのは僕くらいでしたが...)、そのまま第28回WebSig会議「2012年に向けたWeb受託企業の戦略」の2次会に参加してLightning Talkしてきました。SA dayとWebSigでは基本的に同じ話をしました。SA dayのほうが時間が長かったのでこちらではデモがありましたが。

スライドは以下です。

本当はWebSigの本編に最初は行くつもりだったのですが、Lightning Talkで話すこと、つまりは2012年の当社を考えている思考の中で「コミュニティのHubになる」がスライドのタイトルになっちゃったもので、そんな自分がMT Cafeのほうへいかなくてどうすんねん、みたいな。

当社は6月が決算期なので、期首には以下のような方針を立てて取り組み途中なわけですが、上期を総括しつつ後半戦の戦い方を考えている時に「あ、俺たちってHubやんな、コミュニティの。コミュニティに仕事持ってきたり支援したり依頼したり助けたり。」

要するに、ウチは受託中心からパートナー支援中心にシフトチェンジしつつある段階ということです。自分たちが受注して売上げ/利益をあげること中心からコミュニティやパートナーが潤うことで結果として当社もHappyになれるような、そういう姿に変化していく、ということなんですね。今年はMTに軸足をおきつつもCakePHPの勉強会に参加したりMTプラグインWordPressってのを書いたりしました。

来年はもう一歩進めて、コミュニティとコミュニティをつなぐHubみたいな存在になれないか、それって面白そうじゃない? ということを企んでいます。

俺たち、Hubです。触媒です。面白そうだと思ったら、声かけてください。

P.S. Twitterとかにも書いたけど、何かイベント後のレビュー記事とかみんな書かなくなってない? ブログを書かなくなってる気がする。それが良いかどうかはわかんないけども(僕は、書いていこうと思っていますが)。

第2回 CakePHP2.0 勉強会@Tokyoに参加してLightningTalkしてきました。CakePHPのブログチュートリアルを途中までしかさわったことのないなんちゃってCakePHPユーザーにもかかわらず...

実際のところ激しく道に迷って前半聞けてません。後半も細かいことはわかんなかったですが、CakePHPコミュニティの雰囲気はわかりましたよ!

※しかし個人で数百万円立て替えてお祭り主催ってすごいなPHP!

本当はこっち(デザイナー向けCakePHP勉強会 : ATND)で話した方が良いネタな気もしましたが、また機会があればということで(Ustは見ていたのでした)。

LightningTalkのお題はMTCake。CakePHPのViewをMovable Typeのタグ(MTML)で書けるようになるプラグインです。

CakePHPコミュニティとMTコミュニティの話とテンプレートエンジンとMVCの話

かたやフレームワーク、かたやソフトウェアなので違いがあって当然ですがCakePHP2.0勉強会の参加者は開発者(プログラマ)中心です。MTの方はデザイナ/フロントエンドエンジニア中心。プログラマやプラグイン作成社の方が少ないです。とはいえMTってのはテンプレートエンジンやアプリケーション開発のフレームワークを内包していて、開発系の勉強会をすることもできます。LightningTalkした時の反応が面白かったのが「コントローラーとビューは完全に別れています(一部MTMLでロジックも書けるけど)」といった時の反応(そうだよねそうあるべきだよね / それはそれでどうかと思うけどの両方の反応がありました)。

CakePHPの勉強会では必ず(1回参加+Uset鑑賞一回のくせに必ずとか言うなw)MVCの話が出ます。MTの勉強会ではMVCとか話題に出ません(僕が何度かセミナーなんかで話したことはあるけども)。MTの勉強会ではMVCとか概念の話は出ません。いきなりこう書けば、こう出る。 <MTEntryTitle> と書けばブログ記事のタイトルが表示されます、という話から始まって MTIfとかブロックのループとかを話しちゃってます。

これは、Movable Typeのそもそもの由来というか(10周年ですね)プログラマとデザイナという夫婦の会話が関係しています(いや、知らないけど。以下、フィクション)。

いま作っているブログツールなんだけど、テンプレートエンジンをどうしようか迷っているんだ。MVCっていう考え方に...
MVC? MVPならわかるけど野球の話? うーん、エンジンってそもそも分かんないわ車の話とか...テンプレートはわかるわ私ちゃんとテンプレートくらいは用意してデザイン進めるもの。
HTMLはわかるよね。
ばかにしないでよちゃんと勉強したわ。
<TMPL_VAR NAME=ENTRYTITLE>で書いたファイルをFTPでサーバーにアップするってのはどうだい?
そんな呪文書いたファイルをFTPとか...できればFTPも使いたくないわ。あと、EntryTitleのほうが見やすいし、HTMLだったら大文字小文字どっちでもちゃんとブラウザで表示されるし、そんな簡単なのがいいわ。

※ここでHTML::Templateを使うのをあきらめる

オーケイ、テンプレートエンジン、いや、エンジンじゃないな、HTMLみたいなタグで扱えるようにしよう。タグは、えー、そう、君の名前の頭文字をとってMTからはじまるようにしよう!
素敵!

という会話があったかどうか僕には知る由もないが、作ったものをデザイナーに教えるというよりもデザイナーとの共同作業の中で生まれたしくみやルールだからこそこれだけ普及したしデザイナー/フロントエンジニア人口が多いんだろうと思ったり。

特に今のCakePHPの仕組みが良いとか悪いとかMTMLがベストとか思わないけど、MTCake面白いので誰か一緒にやってみませんか? というメッセージを伝えにいったのでした。伝わったかな?

Movable Typeに普通にあったらいいと思うんだけどシリーズ...

とか...

カテゴリの場合"categoryfilter"、ウェブページの場合"pagefilter"、ブログ記事の場合"entryfilter"というベースネームのカスタムフィールド(チェックボックス)を作成、チェックを付けたカテゴリ/ウェブページ/ブログ記事アーカイブは出力されません(という働きをするプラグインです)。entryfilterとかはmt-configで変更できます(環境変数 CategoryFilterBasename / PageFilterBasename / IndividualFilterBasename)。

Safari003.jpg

また、フラグ用の変数はP62,63の予約変数で確認できます。スライドには、確認するためのテンプレートモジュールについて言及しています(分からない人は使わないでも問題ありません)

いや、本が手元にないんですよ、という人のために、予約変数はどこに書いてあるか、というご紹介。

mtディレクトリの以下のlib/MT/ArchiveType/以下の各ファイルにアーカイブタイプ毎に指定されています。

#  lib/MT/ArchiveType/Category.pm

sub template_params {
    return {
        archive_class              => "category-archive",
        category_archive           => 1,
        archive_template           => 1,
        archive_listing            => 1,
        'module_category_archives' => 1,
    };
}

ドキュメントにもあります(下記のページからPDFで一覧表があります)。

でも、でもでも、ソース見ろってのも違うと思うし上記のPDFだってわかりやすいとも言いがたい、というか、今変数がどうなっているかを確認したい時ってないですか? layout-wtt みたいな変数もあるし。そこでプラグイン。

カテゴリーアーカイブのプレビュー画面に <MTVarDump> と入れてプレビューするとこんな感じで表示してくれます。エスケープ済みの文字列をpreタグで囲って表示してくれます。

$VAR1 = {
          'category_entry_listing' => 1,
          'preview_template' => 1,
          'archive_class' => 'category-archive',
          'archive_template' => 1,
          'page_layout' => 'layout-wtt',
          'page_columns' => 3,
          'archive_listing' => 1,
          'category_archive' => 1,
          'module_category_archives' => 1
        };

他にもMTCookieDump、MTQueryDump、MTEnvDumpってタグが用意されています。開発・構築のお伴にどうぞ。

タイトルの『"もっと"よくわかる条件分岐。』の話じゃなくなっちゃいましたが、ま、いっか。

ちぃとばかし気になったので反応しておきます。

以前の記事では、CMSとしてはMovableTypeが優れていると書きましたが、WordPressも全く遜色の無い機能を備えていることに気づきました。

それがカスタムポスト機能です。

これは何かというと、MovableTypeで言うところの「ブログ記事」や「ウェブページ」といった投稿の種類を独自に拡張できる機能です。

たとえば不動産物件を管理するサイトであれば「不動産物件」であったり、旅館のサイトであれば「お部屋」といったように、自由に投稿の種類を追加することができます。 そのため、サイトの新着情報は新着情報として、不動産物件は不動産物件として別々に管理することができるのです。

Movable Typeも実はカスタムポスト機能と言うべき仕組みを持っているのですが(名前はついていませんけど)そのあたりのドキュメントや事例が少ないんでしょうね。少々面倒なことは否定しませんが。

Movable Typeのカスタムポストが少々面倒なのは、MTは静的生成できるからという面もあると思います。独自オブジェクトを作成してリスティングスクリーンを作成、投稿画面を作成して権限を設定、それからArchiveTypeを登録してやらないといけない。

ちなみに、ドキュメントがない、というのは4-5の頃の話で現在はGitHubに詳細なドキュメントがあります。周知不足ですねきっと。

ギークなエンジニアなら、その程度はプラグインを作ってお茶の子さいさいなのでしょうが、わざわざそのためにプラグインを作るコストを考えると非常に手間がかかります。

こいつ↓を使うとプログラムを1行も書かずにカスタムポストが実現出来ます。

有償じゃねーか、って言われそうですが、弊社案件では原価かかんないわけですからWordpressを選択する理由にならないですし。

2つ以上の言語習得が必要なMovableType

しかしながら、逆に複雑なこと・・・例えば「多階層のカテゴリをサイドバーにメニューの一覧として出力したい」等をMTMLで実現しようとすると、プログラミング言語よりも圧倒的に制限された仕様内で試行錯誤を行わなくてはならず、非常に手間がかかります。

できる...よね? MTMLだけで。そう複雑とは思えないですが。

まぁ、この↓ページにサンプルがないのが悪い。

問題はこの部分↓。

さらにテンプレートにはPerlは利用できませんから、プラグインで開発する独自の機能はPerlでMTMLとして記述できるように開発し、MTMLとしてテンプレートに記述するという、2段階の手順を取る必要があります。

これはもう、考え方としか言いようがないですが、プレゼンテーションとロジックが同じ言語で書かれているってのは、テンプレートの意味があるのかないのかわからないんです(分ける必要がないんじゃないかと)。PHPは言語じゃなくてタグだ(<?php ?>)なんて言う人がいたりする? けど、そうでしょうか?

そもそも、同じ人が書くことを前提にするなら学習コストが2倍みたいな話になってくるわけで、ロジックを書く人間とプレゼンテーションを書く人間をきっちり分けちゃえば片方でいいですよね。MTML自体にロジックを書くことも結構できるわけですが、それにしてもプレゼンテーション担当はPerl(PHP)やらなくでも良いわけで。

これまでにもMTCakeとかMTPlugin Wordpressってのを書いたりしたんですが、やっぱりきっちり分けることで分業効率とメンテナンス性が向上しないですかね(すると思いますよ)。

Cakeのテンプレートの例を見ていただきたいのですが(WordPressの話ではないのですが)、ループの部分は <?php foreach ($posts as $post): ?>~ <?php endforeach; ?>、タイトルを取り出すのは配列 $postのキー[Post]のさらにキー[title]をPHPのコードで取得するわけです。これはテンプレートというよりもう、何て言うかプログラムです。

MTCakeを利用した方のテンプレートは、きっちり分かれてますよね。

ビュー

<!-- File: /app/controllers/posts_controller.php -->
<?php
class PostsController extends AppController {
    var $name = 'Posts';
    function index() {
        $ctx = $this->ctx;
        $ctx->__stash[ 'vars' ][ 'page_title' ] = 'Blog posts';
        $ctx->stash( 'Post', $this->Post->find( 'all' ) );
    }
}
?>

プレゼンテーション(テンプレート)

<!-- File: /app/views/posts/index.ctp -->
<h1><mt:var name="page_title" escape="html"></h1>
<!-- Here is where we loop through our posts array, printing out post info -->
<mt:cake:loop model="Post">
<mt:ignore>
    or <mt:cake:loop model="Post" stash="posts">
</mt:ignore>
<mt:if name="__first__">
<table>
    <tr>
        <th>Id</th>
        <th>Title</th>
        <th>Created</th>
    </tr>
</mt:if>
    <tr>
        <td><mt:var name="id"></td>
        <td>
            <a href="./view/<mt:var name="id">"><mt:var name="title" escape="html"></a>
        </td>
        <td><mt:var name="created"></td>
    </tr>
<mt:if name="__last__">
</table>
</mt:if>
</mt:cake:loop>

DynamicMTML(WordPressプラグイン)やMTCakeではテンプレートでPHPも書けますが、これは敢えて書かない方向性で実装していくほうが望ましいと思います。ここら辺はもう宗教思想みたいなところにいっちゃいますけどね。

オブジェクト指向のPerlが敷居が高いってのは...この辺は好みの話になると思いますが、MTの場合はPHPでもプラグイン書けます。DynamicMTMLを使うことで静的ファイルに対する処理のプラグインもPHPで書けるようになります。テンプレートにPerlやPHPでロジックを書かない、これはもう、敢えて書かない方向でいくべきなんじゃないかな、と思うのでした。

最後に、「有償」ってのはどうなんだって話ですが、有償ってのは「責任」と言い換えることができると思います。先日来立て続けにセキュリティアップデートのリリースされたMTですが、WordPressにもこんなのがあって(→「WordPressのプラグインに悪質なコードが混入」)何かあったときに、「オラどうすんだよこの野郎」って言えるかどうかってのが有償と無償の境目なんじゃないかと。

あとね、結局あの悪評高い? 再構築の件ですが、規模が一定以上だと静的配信できないとやっぱ苦しいです。自治体・官公庁に動的配信ベースのものが受け入れられるかというと、やっぱ難しいです(無いとはいいませんが)。

えー、まぁ、何というかセキュリティアップデート続きのおかげもあって今日も仕事してます。ええ。最初は行く予定だったんですけどね。

さて、ちょっと切り替えて真面目に行きます。

仮にテスト・ドリブンの開発を行っていたとしても、バグやセキュリティ脆弱性が見つかることはある程度以上のソフトウェアにとっては現状のところ避けられない問題ではないかと思います。ソフトウェアのコードを読み進むことで開発者が想定していない問題が見つかったりすることは実際にある訳で、最終的にはコードレビューこそが最終的な品質管理だと言えるのではないでしょうか(結局のところ開発者の予期しなかった問題を指摘するのはアナログな手法でコードを読めるテスターの存在頼りだったりします)。

現在、MT開発チームはセキュリティ強化月間らしいです。コードレビューをしましょうよってのは僕も思って提案してたから、良い傾向だと思います。

単にコードレビューといっても、やはり何らかの目的と視点を持ってみていかなければ脆弱性やバグは発見できないことがあります。レビューやテストに実は一番大切なのは「想像力」ではないかと思う今日この頃です。

簡単な例題でコードレビューの視点を考える

例題として「ウェブページに対する操作を行う以下のメソッドのコードレビューでは何を想像すれば良いか」というお題を考えてみましょう。

リクエストの形:
__mode=some_action&blog_id=1&id=100

問題のコード

sub some_action{
    my $app= shift;
    require MT::Page;
    my $page = MT::Page->load( $app->param( 'id' ) );
    if (! $page ) {
        return $app->translate( 'Invalid Page ID:[_1].', $app->param( 'id' ) );
    }
    my $perm = $app->user->is_superuser;
    if (! $perm ) {
        $perm = $app->user->permissions( $app->blog->id )->can_administer_website;
        $perm = $app->user->permissions( $app->blog->id )->can_administer_blog unless $perm;
        $perm = $app->user->permissions( $app->blog->id )->can_manage_pages unless $perm;
    }
    if (! $perm ) {
        return $app->translate( 'Permission denied.' );
    }
    # この後、$pageへの処理
}

例外として想像すべきケースは例えば以下のようなものです。

  1. パラメタidに数字以外のものが渡された場合
  2. パラメタidにpageオブジェクトではなくentryオブジェクトのidが渡された場合
  3. パラメタidが空の場合
  4. パラメタidに現在のblogに属さないentry/pageのidが渡された場合

1.のケースですが、数字以外のものが渡された場合、pageはロードされませんから、 return $app->translate( 'Invalid Page ID:[_1].', $app->param( 'id' ) ); が呼ばれます。return している際にパラメタ id を付けてメッセージを返していますが、idにJavaScriptが渡されたらどうなるでしょうか。このコードにはXSS脆弱性があります。

2.のケースでは、$pageに entryオブジェクトがそのまま格納されます(IDのみを指定した場合、classに関わらずロードされます)。ところが、この後の権限チェックではcan_manage_pagesで権限をチェックしています。このコードには権限のないオブジェクトに対する操作が可能な脆弱性があります。

3.のケースですが、my $page = MT::Page->load();となりますが、$pageには条件指定なしで呼び出されたオブジェクトの先頭1件が格納されてしまいます。操作の内容によりますが、これはバグである可能性があります。

4.のケースですが、entry_idが100のページがblog_idが2のブログに属していた場合どうなるでしょうか。 blog_idが1のブログに対してウェブページの管理権限を持っているかどうかのチェックを行っていますが、blog_idが2のブログの権限チェックはしていません。blog_idが2のブログにこのユーザーが権限を持っていない場合、このコードはそのまま実行されてしまいます。このコードには権限のないオブジェクトに対する操作が可能な脆弱性があります。

その他にも、オブジェクトに対する操作を伴うメソッドには magic_token によるチェック、及びcms_save_permission_filterやcms_delete_permission_filter等操作に応じたコールバックをコールして権限チェックを行わなければなりません。

例題のコードの修正すべき点は以下の通りです。

  1. idがなければその時点でエラーを返す(エラーメッセージに受け取ったパラメタを含める場合はencode_html)を忘れない(もしくはテンプレート側でエスケープする)。
  2. idが数字かどうかをチェックする(数字でなければエラー)、またはエラーメッセージにユーザーが指定可能なパラメタを含める場合、MT::Util::encode_htmlを通して返す(ettor.tmplの方でエラー出力のタグにescape="html"が含まれている場合は$app->errorメソッドを使うことでも良いですが、現状のMTではコード側でエスケープした方が安全)。
  3. 読み込んだ$pageの$page->classをチェックして、page以外であればエラーを返す。もしくはロードメソッドでclass=>'page'を指定して、オブジェクトがpageであることを保証する。
  4. $app->user->permissions()に渡すBlogIDを $app->blog->id ではなく、 $page->blog_id とする(もしくは$app->blog->id と $page->blog_id が異なる場合にエラーを返す*システムスコープで実行される可能性があるケースでは前者)。
  5. この後オブジェクトに何らかの操作を行うメソッドの場合は、magic_tokenのチェックを事前に入れる
  6. 保存、削除を伴うメソッドの場合、cms_save_permission_filter.pageコールバックをコールして、戻り値がない場合にエラーを返す(必要に応じてcms_save_filter.page/cms_pre_save.page/cms_post_save.pageをコールする)

修正適用後のコード(例)

sub some_action {
    my $app = shift;
    $app->validate_magic or
        return $app->trans_error( 'Permission denied.' );
    require MT::Page;
    my $page;
    if (! $app->param( 'id' ) ||
        (! $page = MT::Page->load( $app->param( 'id' ) ) ) {
        return $app->trans_error( 'Invalid Page ID:\'[_1]\'', MT::Util::encode_html( $app->param( 'id' ) ) );
    }
    if ( $page->class ne 'page' ) {
        return $app->trans_error( 'Invalid Class:\'[_1]\'', $page->class );
    }

    # permission check case 1

    if ( $app->blog->id != $page->blog_id ) {
        return $app->trans_error( 'Invalid BlogID:\'[_1]\'', MT::Util::encode_html( $app->blog->id ) );
    }    
    if (! $app->can_do( 'manage_pages' ) ) {
        return $app->trans_error( 'Permission denied.' );
    }
    
    # or case 2
    
    my $perm = $app->user->is_superuser; 
    if (! $perm ) {
        $perm = $app->user->permissions( $page->blog_id )->can_administer_website;
        $perm = $app->user->permissions( $page->blog_id )->can_administer_blog unless $perm;
        $perm = $app->user->permissions( $page->blog_id )->can_manage_pages unless $perm;
    }
    if (! $perm ) {
        return $app->trans_error( 'Permission denied.' );
    }

    if (! $app->run_callbacks( 'cms_save_permission_filter.page' $app, $page ) {
        return $app->trans_error( 'Permission denied.' );
    }
    # Do Something.
}

こんなことが本当にあるの? ってか、でも結構あるんですよね。だいぶ潰れてるとは思いますが、セキュリティ強化月間で徹底的にやっていただきたいものです。もちろん、弊社もセキュリティ強化月間です。

さて、実はもう一つ紹介したい例題があるのですが、さすがにちょっとここには書けないです。世の中には危ないコードがいっぱいありますからね。またいずれ気が変わったら書くかもしれません。

このエントリは全く個人的な興味・関心から書いたもので、実在の(!)人物・団体・会社や自分の会社等とは全く関係がないことを最初にお断りしておきますw

色々と考えることのあるエントリだったので紹介しておきます。

Byrne Reese氏はSix Apartの元中の人で(2004年から2008年まで)MTの元プロダクトマネージャ。現在はMovable Type や MelodyのコンサルティングをしているEndevver, LLCのパートナー。Movable TypeをフォークしたMelodyのチェアマンで開発リーダー。

※以下、翻訳には誤りがある場合があります。

Disclaimer: Byrne Reese is the former Product Manager of Movable Type and TypePad and worked at Six Apart from 2004 to 2008. Byrne Reese is now a Partner at Endevver, LLC, a premiere Movable Type and Melody consulting company, as well as the chairman and a leading contributor to Melody, a fork of the Movable Type platform.

"元"中の人が書いたエントリということで興味深いですし(もちろん元"中"の人であるが故のネガティブなニュアンスが含まれるという面もあるでしょうが)、ブログのリーディングカンパニーであったSix ApartがMovable Typeとともに日本の企業に買収されたということで海の向こうでは"How did WordPress win?"ということになるのでしょう。たくさんのコメントもついています。
このエントリでは様々なことに言及されていますが、大きくは

  1. ライセンス問題(GPL vs コマーシャルライセンスと、Movable Typeの2004年問題)
  2. Perl vs PHP(快適さ、カスタマイズの容易さ、インストールの容易さ)
  3. WordPressのコミュニティ(とAutomattic)
  4. Six Apartの事業戦略

といったことが挙げられています。

ライセンス問題

ライセンスの問題についてはGPL vs 商用ライセンスというだけでなく、2004年問題?について言及されています。

WordPressはGPLライセンス。MTOS(Movable Type Open Source)も現在はGPLで、Byrne Reese氏らが開発しているMovable TypeをフォークしたプロダクトMelodyもGPL、一方でMovable Typeは商用利用についてはコマーシャルライセンスが基本です。

When Movable Type changed its license in 2004, it proved to be a significant turning point for WordPress. Yes, the change angered a lot of people and led to a lot of loyal Movable Type users deciding to switch to WordPress.
Movable Typeが2004年にそのライセンスを変えたとき、WordPressにとっての重要な転機でした。そう、ライセンス変更は多くの人々を怒らせて多くの忠実なMovable TypeのユーザーがWordPressにスイッチすることを決めさせることにつながりました。

* 日本の事情がちょっと異なるのは、本格的にMovable Typeが普及していったのが2004年以降(日本法人が出来、MT3.xが商用ライセンスとして日本で提供されはじめてから)という点でしょう。このあたりは次回以降のエントリで。

PHP vs Perl.

WordPressがPHP、Movable TypeがPerlであることも要因のひとつと述べられています。

Unfortunately it is impossible to avoid the Perl vs PHP debate when it comes to WordPress and Movable Type, and the fact that cogent and compelling arguments can be made and demonstrated that Perl knowledge has never been required, not once, not ever, to build a web site using Movable Type doesn't matter. People simply feel more comfortable working with PHP. And even though the vast majority of people will never have or have ever had the need to hack the source code of their CMS, they are still comforted knowing that they could if they had to. People just never had that kind of comfort level with Perl and by association, Movable Type. Perl is just simply too scary.
残念なことに、WordPressとMovable Typeに関してはPerl vs PHP議論を避けることはできません。Movable Typeを使うことでPerlの知識がなくてもウェブサイトを作れるという議論はここでは重要ではありません。 人々は、単にPHPがより快適であると感じます。また、大多数の人はCMSのコードをハックする必要がない(あるいはこれまでにはそうする必要がなかった)としても、今後その必要があった時の(PHPであることが)慰めになります。人々はその安らぎをPerlやMovable Typeでは得ることができない。Perlはただ単に"怖い"のです。

両刀使い?の自分とすれば、完全に同意はしかねるところはあるものの、言わんとするところは理解できます。僕はこの発言がPerlプログラマであるByrne Reese氏から発せられたことに注目したいと思います。

WordPressのコミュニティとAutomattic

AutomatticとWordPressコミュニティがSix Apart、Movable Typeに対抗した方法についてはこう述べられています。

WordPress defined itself not as superior product by its own merit, but as the underdog. It succeeded by villifying Six Apart, by casting doubt on Six Apart's integrity and by constantly stoking the fires left over from Movable Type's licensing fiasco.
彼らはWordPressを優れた製品としてではなく、弱者として定義しました。Six Apartの完全性に疑問を呈し、Movable Typeのライセンスの失敗の残り火を焚き火して消さないようにすることによってSix Apartを叩くことに成功しました。

あるいは、

They would do whatever it took to move people to WordPress.
WordPressに乗り換えるためのことなら何でもした。

WordPressのコミュニティとAutomatticは乗り換えキャンペーン(それはもちろんMovable Typeからの)を展開したりWordPressに乗り換えるためのことなら何でもしたと書かれています。判官贔屓ってのは日本だけかと思ったらそうでもないようで、WordPressは自らを弱者(underdog)と位置づけ、アンチSix Apart、アンチMovable Typeとして振る舞ったという意味合いなのでしょう。

Apperceptive社の買収とコミュニティの衰退

Apperceptive社の買収についての見解は中々に興味深いです。当時、Aからはじまる会社を買収とかいう話が出ていて結果としてデザインやテクノロジーコンサルティングを行う会社を買収したわけです。つまりはサイト構築やネット広告の企画や開発を行う会社を買収したということです。Byrne Reese氏はこのことがコミュニティに影響を与えたと述べています。

Apperceptive社はニューヨークのデザイン、テクノロジーコンサルティング会社(We are design and technology consultants in New York City. )。

2008年のITメディアのニュースはこちら。

そして、この買収が招いたこととしてByrne Reese氏は以下のように述べています。

Six Apart's Purchase of Apperceptive

Even as Movable Type's community started to become small in comparison to WordPress', its community was still just as competitive. Its community was strong for the same reason that WordPress' was - it consisted of a number of very bright, and exceedingly dedicated community members who were as invested in their respective trades as Six Apart and Automattic ever were.

Six Apart の Apperceptiveの買収

Movable TypeコミュニティがWordPressコミュニティよりも小さくなり始めた時はまだ同じくらい競争力がありました。そのコミュニティは、WordPressのコミュニティと同じくらい強かった - それは、これまでに、Six ApartとAutomatticと同じくらい専心的な何人かの非常に明るくて、非常に熱心なコミュニティメンバーから構成されていました。

Then Six Apart purchased Apperceptive. It was a great business move from a revenue stand point, but the consequences to the community were devistating in the long run. Here's why:
それから、Six Apartは、Apperceptiveを買収しました。それは収益向上の面からの大きなビジネスの動きでした。しかし、長い目で見ればコミュニティを衰退させていきました。理由は、ここにあります
Six Apart's purchase of Apperceptive was successful, by all measure and accounts. Business increased, enterprises flocked to the platform and Movable Type was growing at an even faster clip. In order to meet the demand of the new business though, Six Apart began to hire the smartest and most innovative members from its community into its professional services team. Once hired, all of the awesome work they were doing got swallowed by the increasingly closed and proprietary Six Apart professional services ecosystem.
Six ApartのApperceptiveの買収は多くの面で成功を収めました。ビジネスは増加しました、企業はプラットホームに群がりました。Movable Typeはさらに速いクリップで成長していました。新しいビジネスの需要にこたえるためにSix Apartは能力があり革新的なメンバーをコミュニティからプロフェッショナルサービスチームに雇い始めました。一旦そうなると、彼らがしていた素晴らしい仕事のすべてがますます閉じていき独占的になり、エコシステムはSix Apartプロフェッショナルサービスに飲み込まれました。

そして、結果としてプラットフォームへ群がる顧客たちは個人事業主やフリーランス(independent contractors)が多いMovable Typeのデベロッパーたちではなく、Six Apartが独占的にその仕事等を得るようになり、コミュニティは衰退していったと述べています。

Six Apartの製品戦略について

最後に、Six Apartの戦略について述べられています。

Six Apart's Failure Finally, I will add one more contributing factor to WordPress' success: Six Apart's failure. The reasons behind its ultimate failure as a product company are many, are complex and in many cases very nuanced. But the general consensus is apt: Six Apart severely hampered its own ability to compete effectively by spreading its many exceptionally talented resources across too many products.

In short, Six Apart lacked focus.
最終的に、私はWordPressの成功にもうひとつの要因を加えるつもりです: Six Apartの失敗。製品企業としての失敗の背景は多くの要因により複雑で非常に微妙な違いがあり一概には言えません。しかし、一般的な見解としては: Six Apartは、あまりに多くの製品のに有能なリソースを分散させすぎた。そのことが結果として競争力を弱めた。

要するに、Six Apartはフォーカスを欠いていた。

もちろん、当時のSix ApartはMovable Typeだけを扱っていたわけでなく他のサービスも展開していたわけで、会社のビジネス全体とひとつのソフトウェアの勝敗がイコールであるわけではないのですが、WordPress vs Movable Typeという構図で見ることに限定するとこういう見方になるのでしょう。

10年続いた(もちろんまだ続いている)サーバーサイドのアプリケーションとして希有な存在のMovable Typeと、Six Apartというブランドを作り上げ、それを売却して幾ばくかの富を得たであろう会社・製品が敗者であるか勝者であるかの判断は色々な見方があるのだと思います。このエントリは一つの見方ではありますが、海の向こうでどのように認識されているかについて理解するのに役には立つことでしょう。

一方で日本の事情はちょっと(かなり)異なります。これについてはそれこそフォーカスがずれてしまうので次回以降のエントリで(こちらは自分の見方も入れて)書いてみようと思います。

ウェブページ

OpenID対応しています OpenIDについて
Powered by Movable Type 5.04

このアーカイブについて

このページには、過去に書かれたブログ記事のうちMovableTypeカテゴリに属しているものが含まれています。

次のカテゴリはMovableTypeプラグインです。

最近のコンテンツはインデックスページで見られます。過去に書かれたものはアーカイブのページで見られます。