今はかんたんログイン脆弱性がブームなようです。何言ってるんだという感じですが、一応adiary内部でどのようにかんたんアクセスを処理しているか書いておきます。かんたんログインとはadiaryの実装しかし別の問題が......とりあえずかんたんログインとは携帯の機種固有IDを使用した自動ログイン機能のことです。Cookieを実装する気のない某殿様商売通信キャリアの存在が一気に利用を広げました。機種固有IDは、USER-AGENTなどのHTTPヘッダによって送信されます。HTTPについて知っている人ならすぐわかることですが、このヘッダは誰でも簡単に任意の値で送信することができるため何のセキュリティにもなりません。例えるなら、掲示板に書き込む際の名前欄で承認しているようなものです。比較的単純な通し番号なので、秘匿性もあったものではありません。ほかにどこのサイトにも同じものが送られるといった理由があります。adiaryの実装もともとこんな危険なものを実装する気はなかったのですが(苦笑)、要望があったのでしぶしぶ実装しました。右の画面がかんたんログインの実際の画面です*2。このページをブックマークしてもらい「かんたんログイン」をクリックしてもらうことでログインできます。ID:useridの人がかんたんログインを設定したこの画面のURLは次のようになります。http://~/adiary.cgi/=userid:LE0GdeSwSdQ/?login_docomoLE0GdeSwSdQという文字列は「機種固有IDから生成されるハッシュ」です。'秘密の言葉'>の設定が漏洩しない限りハッシュ文字列を第3者が知ることはできません。仮にこのログインURLだけ漏洩しても、対応する機種固有IDが分からないとログインできません。いわゆるかんたんログイン脆弱性はadiaryには存在しないことが分かります。この画面はDoCoMoの場合。埋め込まれており機種固有IDの送信を要求している。別の問題が......次は携帯ログイン後のURLです。http://~/adiary.cgi/=userid=kpQYloloFFF0M4Xnujjs/?login_docomo「kpQYloloFFF0M4Xnujjs」がセッションIDになります。Cookieが使えないので仕方なくURLに埋め込んでいるのですが、リファラーからこのURLが漏れるとセッションが乗っ取られてしまいます。ドコモはリファラを送らないことになっているみたいですが、他機種だと色々で頭の痛い問題です。(参考)携帯電話向けWebアプリケーションのセッション管理手法とりあえずCookieを食わせて、食べられたらそっちに誘導するように変更するか......。Cookie非対応でリファラ送信なんて機種もあるそうですが、それは端末脆弱性なんで無視かな。実装について詳細に解説します。モジュール自動更新の必要性SpeedyCGIやmod_perl等々のスクリプトキャッシュ環境で実行速度が改善するのは、プログラム本体やライブラリをコンパイルした状態で内部に保持するためスクリプト実行時間の大半を占めるコンパイル時間が短縮されるというメリットがあるためです。最近のスクリプトは便利なライブラリを使用する傾向が強く、実行時間の95%以上がスクリプトのコンパイル時間なんてこともめずらしくありません。常駐プロセスを手動で殺したり、Apacheを再起動させたりといった手間が必要になります。これを避けるためにはスクリプト自身でライブラリの更新をチェックし、ライブラリが更新されていた際に自動的にスクリプトを再起動(リロード)させる仕組みが必要になります。adiaryはPerlの付属ライブラリを極力使用せず、車輪を再発明しまくることによって実行速度を向上させています。その仕組みが実装されています(むしろそのファイルは、それ関連しか書かれてません)。ライブラリの更新時刻を保持するハッシュです。呼び出して、更新されたライブラリがあるか確認しています。更新されたライブラリが発見された場合は、この実行終了後に(キャッシュされた)プロセスを終了する予約をして、リクエストされたURIにリダイレクトします。同一URLにリダイレクト}}FastCGIでの処理も同様です。させるわけにはいかないので、別の方法になっています。更新が検出できない(リロードされない)というバグに長いこと悩まされていました。何分かアクセスがなければ自動でプロセスが落ちるためさほど深刻ではありませんが、奇妙なので少し調査してみました。SpeedyCGIの起動ルーチンは次のようになっています。リロードされないということが判明しました。本当にリロードしているわけではなく、use段階は再実行しない(perlのBEGIN構文)ようです*3。部分を次のように書き換えることで解決されました。ロードの途中に構文エラーなどで失敗した場合、requireしたライブラリにロード済の情報が残ったまま($INC{"Satsuki/Base.pm"}が保存されたまま)の状態でキャッシュしてしまいます。注:cgiファイルを書き換えた場合は、useも含めて再実行されます。コメント(0件)2007/04/28(土)ページ送りのためのデータベース検索adiary2::内部構造adiaryにGoogleのようなページ送りを実装できないかというお話がメーリングリストでありました。実は速度面で不利なので実装してなかったんですよね。どれくらい不利なのか調べてなかったので検討してみました。その前にクイズadiaryではご覧の通り「次のページ」と「前のページ」は(存在すれば)常に表示されるようになっていますが、実は全件のデータを取得することなく表示しています。やってるか分かりますか?分かってしまえばかなり単純な仕組みですが、ご覧の通り十分実用的な動作をしています。実行速度の検証データ数が少ないときは気にするほどの差はないので、とりあえず810件、1MBほどのデータをインポートしてテストしてみました。ms検索は「あ」一文字の全文検索という、多量にデータが引っかかるような検索を行いました。500MHz(笑)擬似データベース(pseudo)件数取得の全文検索以外は大したことないですね。擬似データベースはインデックスデータを常にメモリに展開していますので、全文検索しない限りは個別のデータファイルを参照にいきません。数件しか取得しないのならば、数件発見した時点で個別データのロードを終了するので、通常のトップ表示と比べせいぜい10ms程度しか違わないわけです。件数取得の検索は、810個の個別のデータファイルをすべてロードすることになり、多量の時間がかかっている。付いているのは、cgiがキャッシュされている場合です*1。この場合、一度でも読み出したファイルはメモリにキャッシュされますので810件のファイルを開く必要がないので速く済んでいます。それ以外は、結局メモリに展開されている(1ファイルになっている)インデックスデータをループで回すだけなので、時間的にほとんど変わらない。(Pg)総じて擬似データベースより少し良いパフォーマンスという感じですね。件数取得に時間がかかっていますが、PostgreSQLでは、件数取得を指示された場合、そのためだけに1回クエリを投げる実装になっています(Satsuki-systemの実装の話です)。~件数取得をすると時間が増えてしまうのは、単純に検索範囲が増えた結果なのか......難しいところです。こんな速いとは思いませんでした。MySQLはありがたいことに、検索時にヒット件数を取得する書式があり、ほとんどオーバーヘッドなく該当件数を取得出来ます。微妙なところですねぇ~。気にしなくていいと言えば気にしなくていいような、気にすると言えば気にするような。動作を考えると、ちょっとなぁ......。その他、擬似データベースは結構チューニングしてあるのですが、その甲斐あってか思ったより高パフォーマンスでびっくりました。1000件ぐらいなら余裕で使えますね。これ見てるadiary利用者の方々は「MySQL速いんだ、ぜひとも使おう」とか思いませんように。して動かす限り、MySQLの速さの恩恵にあずかるどころかMySQLのモジュールロード時間で恩恵どころか遅くなってしまいます(PostgreSQLも同様)。以外の方法で動作させてからにしましょう。件数取得せずにページ送りを出す方法クイズの答え。1ぺージ5件表示だとしたら、検索するときに6件のデータを取得します。検索結果として6件のデータが得られたら次のページが存在すると分かります。前のページがあるかどうかは、検索開始位置が1件目であれば前のページがない、2より大きければ前のページがあると判別します。単純でしょ?コメント(0件)2006/07/08(土)Satsuki-system技術情報adiary2::内部構造Satsuki-systemの文法等はこちら参照はてな技術者のnaoyaさんのPerl技術記事見てたんですがadiary関連の技術情報=マニュアルも本格的にまとめ始めたほうかいいなぁーと思ったりしたりしました。数日中にもβ版公開しましすが、ソースみただけではわからないところが多々ありますし。比較的短期間かつ少人数(一人か)でこれだけのシステムになったのかってのは訳があって、生産性を担保するシステム構成によるところがとても大きいのです。Satsukiシステム自体も、adiary開発の過程で大きく進化したわけですが、基本コンセプトは何ら変わっていません。Toolkitの比較スケルトンシステムというのは何度も申し上げているとおりで、Perlの変数埋め込みを一歩進めて簡単な命令を書けるようにしたものです。Toolkitがあります。Toolkitは変数置換の他、簡単な条件構文やループなどをHTMLテンプレート中に記述できます。Toolkitでは読み込まれたHTMLファイルを、Perlで実行可能なプログラムとしてテンポラリに吐き出します。次以降は、そのコンパイルされたファイルを実行するわけです(mod_perlのときはキャッシュされるのかな?)。Satsuki-systemも紆余曲折の上、現在はその形に落ち着いています。Toolkitは決定的に違うところがあります。Toolkitのキャッシュはperlプログラムとして実行することでそのまま出力HTMLが得られますが、Satsuki-systemのそれは、そのまま実行することは不可能なプログラムになっています。Satsuki-system本体とコンパイル済のプログラムの2つが合わさって初めて意味を持つわけです。なんだ「使いにくいだけじゃないか......」と言われるかもしれませんが、実はそこが最大のミソなのです。Satsuki-systemはスケルトンシステムでありフレームワークでもあります。スケルトンシステムから参照する必要なルーチン(ライブラリ)は、システム本体に納められており、そのライブラリ部分を自由に拡張できることこそが最大の特徴です。以下はスケルトンのコメント表示部分を抜き出したものです。t2.tm)>)</span></p>強調表示のところが埋め込みです。大半はただの変数置換だと思って問題ありません。関数呼び出しとなっていて、この実体はSatsuki-system本体(Base.pm)に含まれます。この関数に相当するものを増やしていくことで、システム全体を構築します。Toolkitキットでは「Perlプログラムの付属品としてのHTMLテンプレート」であったわけですが、Satsuki-systemでは「スケルトン(HTML)の付属品がPerlプログラム」なのです。厳密にはちと違いますSatsuki-systemとPHPの比較そこまで、スケルトン万歳ならはじめからPHPみたいな埋め込み言語を使えばいいじゃないかという話になります。PHPは使ったことはないのですが、一つそして決定的にPHPを使いたくない理由があります。プログラムとHTMLのソースが一緒になっててメンテしずらい。書かれた(?)PHPのソースならばそうはならないのかもしれませんが、1画面=1HTML=1プログラム主義のように感じます。いうわけで、もちろんSatsuki-systemだって同じだろうというツッコミもあります。adiaryの日記一覧のような部分は、実際にHTML内にPHPのようにプログラム的動作を記述してページ送りなどを実装していますから、同じといえば同じです。それは推奨される形ではなく、ほとんどの場合1ページに対して1つのPerlのサブルーチンが付属しています。RoRのモデル・ビュー・コントローラー的な考えからいえば、Viewがスケルトンで、ControllerがPerlのコードということになります。PHPとの大きな違いはここですが、PHPのプログラム部を外部ファイルとして追い出したとしたら(本体には呼び出しのみ残る)、似たようなものだと思います。比較も何も......似たもの同士です*4。これは別にRoR的思想を取り込んだわけではなくて、Satsuki-systemの「変数埋め込みHTMLの拡張」を突き進めた結果、似たようなものにたどり着いたというわけです*5。最初の思想からMVCなRoRとはスタート地点で負けているわけですが、そこを敢えて比較するとSatsuki-systemはPerl用である。YARVが登場すると立場逆転しそうです(汗RoRの方が思想的にOO(オブジェクト指向)として綺麗である。そもそもPerlのOOはやっつけなので、これもスタート地点で負けてます。今この時点においては、Perlの方が速く動作する。*6Perlの方が普及してるし、ユーザーも多い*7必ずしも「データ(データベース)」対応でなくてもよい分、Satsuki-systemの方が汎用的かもしれない。(prefork/worker)でもcgiでもSatuki-systemが差を吸収してくれる(ただし若干気を遣って書く必要はあり)ですけども。簡単なフレームワークスクリプトを後々作成するといいかなぁ。機能ありすぎ&内部構造まで書き始めると、いろいろと細かい技がありまして(^^コメント(2件)2006/06/23(金)6/23版スナップショットadiary2::内部構造スケルトンコンパイラの改良スケルトンコンパイラをさらに改良しました。今回の変更は多大な労力を割いた割に、テスト環境においてたった1msしか高速化しませんでした*1。コンパイル時間は絶対遅くなっていると思いますが......、1度コンパイルすればキャッシュされるので気にしない方向で(汗)しかし、スケルトンシステムの仕様は、後々コンパイラ次第である程度性能を改善出来るよう設計していました。今回たまたま気になって、泥沼に入ってしまったのですが、改良に改良を重ねた遍歴で用のない(スマートでない)コンパイル処理を何度も行っていたためスリム化。いう、とてつもなく環境にやさしい(遅い)CPUで今回の変更を試してみました。古いsnapshotと比較したら850ms→650msぐらい。