2006-08-01 [長年日記]
_ tracのanonymous権限をほとんど削りました
いくつか置いてあるtracの設定を、ticketとかwikiとかをanonymous権限でもある程度いじれるようにしてあったんですが、tracを対象にしたspamが非常に多く、特にticketに関しては通常のインターフェースでメンテナンスすらできないし、wikiはメンテナンスはできるけれども数が多くていちいち対応してられない感じなんで、anonymous権限での操作は基本的に禁止にしてしまいました。本当は1470.net系のバグ管理で使おうと思っていたんだけどなー。もはやspam対策が充実していないと、オープンなバグ管理システムとしては使えないか。結局自作になるのかなー。
_ MONO発売日カレンダーを追加しました
MONOの発売日カレンダー表示を追加しました。サイドバーなどから今日発売のMONOのようにリンクが張られています。あとトップページにも近日発売予定のMONOを掲載しています。
掲載対象となるのは、1470.net上でメモされた or 検索された or フィードから取得した結果、キャッシュに登録されているMONOのみですので、すべてのMONOが対象となる訳ではありませんが、Amazonの新商品リストは自動的にチェックするようにしておくので、たいていの新商品は載ってくると思います。
あと、発売日が未来で日付単位まで確定しているMONOのメモを取る際には、自動的にToDoに発売日を挿入するようにしました。発売日カレンダーをざっとながめて、欲しいものを適当にメモしていく、といった感じの使い方を想定しています。
今回は、テーブル構造を含めて結構大規模にあちこち変更したんで、もしかしたら何か不具合が出ているかもしれません。何かあったらここのコメントででも教えてください。
2006-08-02 [長年日記]
_ MONO発売日カレンダーの表示単位を日単位に変更しました
最初は月単位の表示で十分だと思っていたんですが、新製品情報をまめにチェックするようにしたら、とてもじゃないけど月単位では表示しきれない感じだったんで、日単位で表示するように変更しました。
2006-08-05 [長年日記]
_ MONO関連地味な改良
発売日カレンダーの表示を、Amazonの商品カテゴリー別に表示できるようにしました。2006年08月05日発売のMONO[Book]みたいな感じです。
あと、MONO検索の際のソートオーダーを、発売日が新しい順に変更しました(デフォルトは売れ筋順)。
2006-08-07 [長年日記]
_ spammerな感じの人が元気にメモを投稿しているけど
どうしようかなー。削除するのは簡単なんだけど、ひとまず何をやるつもりなのか、しばらく様子を見ておこう。はてなの方のアカウントが先に無効になったりするかな?
マルチアカウントでばりばり投稿したりしない限りは、別に消さなくてもいいかなーと思っているんだけど、大量のマルチアカウントはさすがにうざいしなー。
ちなみに、HTTP_USER_AGENTにAlexa Toolbarとか出ているあたり、いかにもそういう方面で飯を食っている人って感じだ。
spam用に自動取得したっぽいアカウント一覧
apple5001sweet apple5002sweet apple5003sweet apple5004sweet apple5005sweet apple5006sweet apple5007sweet apple5008sweet apple5009sweet apple5010sweet egg5011sweet egg5012sweet egg5013sweet egg5014sweet egg5015sweet egg5016sweet egg5017sweet egg5018sweet jdkfnaf840 johyywrph3 jflnfowjr378 ggdnwgri8027 ktogqiwti95829 glejpqrh72 hgoti4woi9y0 nlghwkehjfut673 thfrlghr0p06jd ghhsjrjlu26496 i3otywkrjg gkerhtpey kjirdlofi839tke fo3u4joth5i sjfjg3uykfor yoejeu3pru yoyyp897r9 hro2gti4i2 jldnwo2u56of kjdi3ojojwir hi5u0utofji jyo6kifhk gljpskdt3o kgkpdmwi80 nwiry4ih2y5b i6mjeoe4g nhgitjew1 itj4ofj2jyp65 j79y39jf0 ju6032jf jw94756ufki0 jtej3o47ut ghjo3u8njg j5igonjflrh kjrnhelwo5o3y4 gkthjlrhu7ti7o fkrbt35o6urn970 jtkt8i06u3y jfhek3696 jirege6294 johrky36469 jfehe954hglrh ghekjr3996i rhjritp935 joeh370th tmjpo3y5mj hgo0tjep3 jfjt385 jgjrl303y j3ugjrhlryi9y hkebhelto9y jgklbrl720n jfkrbelk0u jele2029jo0 mjro392yt72 kgej730io mrldi54hridh90 fs4hd64vfiwsg high2000low dokodemo55 fhuebl87sgju lajow57839 woi8999cb dowcbksa8 sjuhasu982r5 zcxv87a sk8dh7rt6sf4 sga6dhj4eoy8 aksj133dhw tekuteku123po weoi837264 gjro98bn world9999wlfh cat22dog11sdu aoshd76234hb s6fugh947fhvo s7fj38fyj29ryb asug231g0y78u g4y6u8kj9lt3ad2 db37jgy4947alo uniextra129873 aiyt2736sdhfg10 awawawa6555g fhy2938hgdiuw asiyg37rg58yj7 qiwu3323gvhas3 piropiro101010 d64ht8hj69r d4f5g6h7j89k9 ko8976hg54td aqwsugd328746 w4e5r6t7y8u8 asidu2376sdhgf oity87kj87hyhh7 sonnnani293846 power8787jcdh t5y6u7i8o9i8u7 woeofuhdbf56r4 aq1sw2de3rf4t gt5hy6ju7ki8lo9 asiudg76dufhg8 asd6g7h8h89j qopqop068068 s2f5j7l8j5d2 sodiasiugd655 austfd387465h as6fy8jy9k80g o9i8u7y6t5r4dd gdhfgr764hdfg asiuy23765ahs s5d6f7g7h8 flash8080rush school2837 q728eh35rhfg qw87yweuhy9 asd_ouhas344 as623g9th0ppp porepore6868 aq37fhsi8eq m9n8b7ytfq super28376shgdf i1j1taji1asd1 do9068cdo zwmnzwmn37 uth74h63g_0e asuh7gf83nc2 s35inmwp49u l95ngu36_38fn rtyry37t7ugn bvbv65hgb7 ikdomwiesd10 ikdomwiesd11 ikdomwiesd12 ikdomwiesd13 ikdomwiesd14 ikdomwiesd15 ikdomwiesd16 ikdomwiesd17 ikdomwiesd18 ikdomwiesd19 ukkkigzass54 ukkkigzass55 ukkkigzass56 ukkkigzass57 ukkkigzass58 ukkkigzass59 ukkkigzass60 ukkkigzass61 ukkkigzass62 ukkkigzass63 blackblack0728 deunfhh3481 uyyhsxx344i zawuwssn18 nvhfgklx854 mxhfuyeut6 lseetfhdy56 mcnxsye645k uryerurhfje5 oii2lsiekoe pjieirtutir84 etryfjdkvmf5 mcddddjx65 kiujfnghdfy cvbmvky65i ddedsweer12 dffghh1235h isksdyefhanx ieutueye2346 siuethw234mc sewcxdarsf43 ofkhiyjh876 okimnbhg65e3 ujdhfnfmv76 etrfyfhdbd56 lkweret52639 opytrye464 qweqw8762 cndhey567we sweqrsy55221 cmdjfarw2387 mznceyq425 dfeei446720 wieufiekd219 woiexjhe2334 oiuqqkx262dh mwdhjki16286 yrujj87919djw lks11333ksud73 wecjks7c3nf92 u73nc6whr5 c7s8ejx4ee w7d5vg7c43 ls347de8 l2s9dek7r355 iwe88823dddd etwoio343258n wieueudueb iwje2833873 vyruy36576iyi x223445558n jieo287hjj76 qqaawwss0001 qqaawwss0002 qqaawwss0003 qqaawwss0004 qqaawwss0005 qqaawwss0006 qqaawwss0007 qqaawwss0008 qqaawwss0009 qqaawwss0010 wwsseedd0001 wwsseedd0002 wwsseedd0003 wwsseedd0004 augjf93847 sidjr84758 flgkt98473 dhtik94637 sigkr64758 sigur84736 ghuth99987 seirn56565 ghjfu47567 thfur66657 vbght85746 vbfhg74352 qwert74636 kijuh76453 hhjfh34563 poiuy78639 yturi88876 urhfy56471 udhrn74633 vbcnf63471 lkfjr09876 erydb54356 rtfgv43621 cvdfe55555 xvdcf34627 yhfbg09340 unfhg66866 cbvgf64536 asdfg93847 hjgkh12121 qwhjo74377 tytyr55575 bnmmn96789 rvvbf77412 mkjio78654 lpoik97534 cvfhr34534 ifkrj47583 cbvhf76384 vjguti99999 ksjrm39845 cjfnr87364 akdir39485 fjfjf94949 kglto99874 poium43562 ifmrj38475 jfhgu77771 porjt84637 dlrkt84950 aaskk94857 plfor93847 forkt93748 okfir84958 ckfit94857 aisurj39485 fjrut87634 sifir88888 sssir87485 sifun83746 aisur47364 hwlorje4i9dg qwiyg763jhs ksieudfyy37cjnw lslsslsl1344lsi zzowuueksl5
上記アカウントの人が投稿したURI一覧
(後で書く)
2006-08-14 [長年日記]
_ RSS経由で取得したタイトルをURI情報として利用する場合
従来、RSS経由で取得したタイトルなどの情報を、そのままURI情報として利用していましたが、ノイズがあまりにも多いようなので、RSSのURLとアイテムのURLのホスト名が一致する場合のみ、RSS経由の情報をURIなどに利用するように変更しました。
_ Zend_Db_TableでdescribeTableを使う場合と使わない場合のベンチマーク
Zend_Db_Tableで、毎回describeTableでテーブル情報を取得する場合と、class定義にあらかじめテーブル情報を記述しておいた場合の、速度比較。
サンプルテーブル定義
CREATE TABLE memo ( id int(11) NOT NULL auto_increment, user_id int(11) NOT NULL default '0', title varchar(255) NOT NULL default '', rgdt datetime NOT NULL default '0000-00-00 00:00:00', updt datetime NOT NULL default '0000-00-00 00:00:00', PRIMARY KEY (id), )
サンプルコード
$db = /* デフォルトDBアダプター */;
Zend_Db_Table::setDefaultAdapter($db);
// 標準のZend_Db_Table
class Memo extends Zend_Db_Table {}
// テーブル情報をあらかじめクラス定義で記述
class Memo2 extends Zend_Db_Table {
protected $_name = 'memo';
protected $_cols = array(
'id' => 'id',
'user_id' => 'userId',
'title' => 'title',
'rgdt' => 'rgdt',
'updt' => 'updt',
);
}
// シリアライズしてファイルに保存したテーブル情報を利用
class Memo3 extends Zend_Db_Table {
public function __construct()
{
$serializedColumns = './columns.txt';
$this->_cols = unserialize(file_get_contents($serializedColumns));
parent::__construct();
}
}
$start = microtime(true);
for ($i = 0; $i < 1000; $i ++) {
$table = new Memo();
}
$end = microtime(true);
echo 'Memo: ' . ($end - $start) . "\n";
$start = microtime(true);
for ($i = 0; $i < 1000; $i ++) {
$table = new Memo2();
}
$end = microtime(true);
echo 'Memo2: ' . ($end - $start) . "\n";
$start = microtime(true);
for ($i = 0; $i < 1000; $i ++) {
$table = new Memo3();
}
$end = microtime(true);
echo 'Memo3: ' . ($end - $start) . "\n";
結果(3回分)
Memo: 3.4024510383606 Memo2: 0.2038938999176 Memo3: 0.67136812210083
Memo: 3.6163651943207 Memo2: 0.20920920372009 Memo3: 0.87713384628296
Memo: 3.3675861358643 Memo2: 0.20081806182861 Memo3: 0.63753986358643
2006-08-17 [長年日記]
_ 18:15〜18:30までサーバーが止まっていました
フロントに立てたpoundの動いているサーバーが刺さってしまい、18:15くらいから先ほどまで1470.net関連のサービスが停止していました。申し訳ありません。
それにしても、このpoundを動かしているサーバーはそろそろやばそうだなー。サーバーを切り替えることを本格的に考えてみるべきだろうか。
2006-08-18 [長年日記]
_ Zend Frameworkの方向性
今日のZendのセミナーでZend CTOのZeev Suraski氏の発言のうち、Zend Frameworkに関する部分を思い出しつつ書いておく。
- (PHP本体と同様に)とにかくシンプルさがポリシー
- 使いやすい、エラーが出にくい、互換性が高い、メンテナンス性が高い
- よく使われる機能(20%)だけに注力
- それ以外は拡張性で対応
- 使いたい機能だけ使えばいい
- 他のライブラリなどと一緒に使えるようにする
- よそのフレームワーク(PHP用のものも、それ以外も)は、アイディアレベルではチェックして、良いものは取り込む
- コードレベルでは他のフレームワークやライブラリのものは絶対取り込まない
- 高品質を保証する
- 知財の問題を回避する
- コードレベルでは他のフレームワークやライブラリのものは絶対取り込まない
- 利用例まで含めたドキュメント作成にも力を入れている
- 高品質で必要なコンポーネントのみで構成される
- PEARのような幅広いライブラリレポジトリとは違う
- 将来的にはZend Coreの一部を構成するらしい
- 追加予定の機能
- Ajaxとイベントモデル → たぶんAjax(クライアントサイドのロジック)からの通知をPHP(サーバーサイド)でイベントとして処理する仕組みだろうな。
- JSON、Widget → ってなんなのかいまいち分からないけど、WidgetってのはZend Studioとかでもサポートするとかいっていたし、上記と含めてビジュアルコンポーネントっぽいものなのか?
- スケジュール
- 今年中には1.0.0を出す予定(他の人は9〜10月と言ってたけど、本人は言ってなかった気がする)
- 現時点でも品質は保証するが、APIレベルでの互換性は保証できないので、今から使う人はそのあたりを自己責任で
PEARとは全然違うんだと強く言っていた。「疎結合のフレームワーク+流行りの機能は取り込むよ」というZend FrameworkとPEARの違いって、使う側からすると大して変わらない(俺的には、PHP5フル対応という点が一番大きな違い)んだけど、作る側(特に企業名を出して)からしたら同じに見て欲しくないってことかな。
ただ、ユニットテストしているから品質が高いと繰り返していたけど、そういうもんでもないよなー、実際。まあPEARよりも品質を気にしているのは分かるけど。
そういやPEARのことはいつもは「ピア」と読んでるんだけど、質問するときには正しい発音通り「ペア」といってみたのに、他の人はみんな「ピア」に近い発音をしていた気がするな。
2006-08-19 [長年日記]
_ PHPカンファレンス2006
フレームワークのパネルでZendの回し者になってきました。ネタはいろいろ用意してあったけど、話の流れでほとんど言う暇なかったな。
最後のZeev氏のプレゼンが終わるまでいたんだけど、懇親会までの待ち時間の間に、眠くて吐き気がしてきたんで、懇親会には出ずに帰ってきてしまいました。挨拶できなかった方々すみません。
そういやZeev氏がプレゼンの最後の方で出していた、ZActiveRecord(今はZend_Db_Table)とかZMail(今はZend_Mail)とかZSearch(今はZend_Search_Lucene)とかは、Zend Framework 0.1.1が発表される以前のちょー古い仕様で、そこで使われていたサンプルコードは現状のZend Frameworkのコンポーネントとは全然違っているんで、あれは信用しないように。っつーか、いったいZeev氏はいつのプレゼン資料を使い回しているんだ? あれを見てちょっとげんなりした。
一応事前に準備しておいたネタ帳は以下のような感じ。
- [各フレームワーク] 現状
- Preview 0.1.5が7/10に出た。
- 高機能な(RoRやSymfonyのような)フレームワークとしてはまだ足りないが、基本的なコンポーネントは一通りそろっている。
- Zend Studioとかと連携しての開発支援系は考えているらしい(昨日のセミナーネタ)。Widgetとか言っていたけど、詳細不明。
- 完成度は、コア部分は実用レベル。流行りもの系(フィードとかWebサービスとか)はまだビミョー。実際に使う人が増えないと、この辺の品質は上がらないんじゃないかな。たぶん開発者もテストケースとかは書いているけど、アプリケーション風の使い方はしていないように思う(結構基本的なところでダメだったりするし)。
- [各フレームワーク] 今後
- 一応今年中には正式版1.0.0が出るらしい。今年9、10月あたりという説もある。まだコンポーネントが足りてない気がするけど……。
- 開発の様子は、SubversionレポジトリやMLを見ている限りでは、ちょっと停滞気味かも。がんがん新しいコードが追加されている感じはない。
- proposalはいろいろ出ている。
- バグ管理とかドキュメント管理とかの仕組みも、ようやく安定してきた(それまではいろんなツールをとっかえひっかえ状態だった)。
- PEARが5〜6対応を推し進める方向に向かっているようなので、そちらとのバランスを取らないとリソースの無駄遣いになりそう。と思ったんだけど、PEARとZend Frameworkは全然別物だし、品質とか知財の関係とかで、PEARのような既存のライブラリをZend Frameworkが取り込むことはないそうだ(昨日のセミナー)。
- [meta] PHPフレームワークは普及するのか?そもそもフレームワークを使う価値はあるか?
- 基本的に、フレームワークの考え方自体はあらゆるシーンで使える。フレームワークは先人の知恵がコード化されたもの。
- 実際の制作物にフレームワークを使うかどうかは、ケースバイケース。ただし、環境さえうまく整えることができるならば、かなり小規模なケースでもフレームワークを採用するメリットが得られる。逆に環境が用意できない場合は、フレームワーク的な考え方だけを利用するか、あるいは小規模な自作フレームワークもどきを使うなどもあり。
- [meta] フレームワークの選択基準
- フレームワークを知らない人は、何でもいいからWebアプリケーションフレームワークの考え方を身につけた方がいい。ある程度メジャーならばなんでもいい。
- 実用レベルでの採用を考えると、コードの品質、採用実績、将来性、環境、パフォーマンスあたりが検討材料となる。
- うちの場合は、PHP5への完全移行を決め、それに伴い環境的にPHP5にフル対応しているものを選択し、その中で将来性、コードの品質を重視してZend Frameworkを選んだ。
- コードの品質というのは、PHPのライブラリでは、バグの少なさよりも、いざというときにソースを読んで自分で対応(修正)しやすいかどうかが重要。もちろんバギーすぎるのは困るが、ドキュメントも実績も十分ではないPHPのライブラリでは、自分のコードと同程度にライブラリのコードを追う必要がある。
- そういう意味では、コード規模がまだ小さく、きちんとした規約に基づいて書かれ、異常系処理が例外ベースで統一されたZend Frameworkが、相対的にベストだった。
- 現時点だけで言うと、他のフレームワークの方が便利なことが多いとは思う。
- [各フレームワーク] バージョンアップ...どう?
- まったく不明。まだそのあたり(運用後のフレームワークのバージョンアップ対応)まで考えるレベルまで来ていない。
- ただ、設計的に各コンポーネントの結合度が比較的低く、インターフェースもきれいな設計のものが多いので、比較的未来は明るいんじゃなかろうか。
- [各フレームワーク] アプリにどこまで食い込むか?(コアonly or CMS方面へ...)
- 現状では、アプリケーションレイヤーよりのフレームワークというよりは、独立したライブラリレイヤーのフレームワークといった位置づけ。というか、疎結合で部分的に他のライブラリと差し替えて使えるように設計してある(昨日のセミナー)そうだ。
- ただし、ZAppのようなアプリケーションフレームワーク的な方向のものもproposalとしては出てきているし、Zend自体もZend Studioと組み合わせての開発支援は考えているみたいなんで、Rails的なアプローチではなく、たぶんマイクロソフトがVisualStudioでやっているようなアプローチに近い支援機能がくるんじゃないかなー。
- ちなみにうちで作っているWEBXPというフレームワークも、Zend Frameworkのコンポーネントを組み合わせてアプリケーションレイヤーよりの作業を効率化するための仕組み。Zend Frameworkは、フレームワーク on フレームワーク的なものを作るのに向いている。
他にも現地でパネルが始まるまでの間にいろいろ追記したんだけど、W-ZERO3[es]が熱暴走して追記した分が失われてしまった。Zend Frameworkを選択した理由とか、他のフレームワークと比較しての特徴とか、いろいろ書いていたんだけど。
_ そういや
Zend FrameworkがOracleにいつ対応するのか質問していた人がいたけど、現時点でもZend_DbにはZend_Db_Adapter_Oracleとか入っているけど、これがちゃんと動かないってことなのかな? PDO OracleドライバーもすでにPHP 5.1.4とかには入っているはずだけど、Zend_Db_Adapter_Oracleがあるから、わざわざZend_Db_Adapter_Pdo_Oracleの方は作られないんじゃないかなー。
_ PHP 6のUnicodeサポート
昼飯の時に「たぶんバイナリ文字列とUTF文字列という二つの文字列型が追加されて、文字列処理関数は型を見て処理を振り分けるんじゃない?」と100%想像で言ってしまったんで、気になってPHP6-devのソースを見てみた。
zend_variables.cの
switch (Z_TYPE_P(zvalue) & ~IS_CONSTANT_INDEX) {
case IS_CONSTANT: {
TSRMLS_FETCH();
if (UG(unicode)) goto dtor_unicode;
}
case IS_STRING:
CHECK_ZVAL_STRING_REL(zvalue);
free(Z_STRVAL_P(zvalue));
break;
case IS_UNICODE:
dtor_unicode:
CHECK_ZVAL_UNICODE_REL(zvalue);
free(Z_USTRVAL_P(zvalue));
break;
case IS_ARRAY:
case IS_CONSTANT_ARRAY:
case IS_OBJECT:
case IS_RESOURCE:
zend_error(E_CORE_ERROR, "Internal zval's can't be arrays, objects or resources");
break;
case IS_LONG:
case IS_DOUBLE:
case IS_BOOL:
case IS_NULL:
default:
break;
}
あたりを見ると、やっぱり従来型のバイナリ文字列とUNICODE文字列の二つの文字列型になるみたいだね。で、各文字列処理関数ではzend_builtin_functions.cから適当に抜き出した、
ZEND_NAMED_FUNCTION(zend_if_strlen)
{
zval **str;
if (ZEND_NUM_ARGS() != 1 || zend_get_parameters_ex(1, &str) == FAILURE) {
ZEND_WRONG_PARAM_COUNT();
}
switch (Z_TYPE_PP(str)) {
case IS_UNICODE:
RETVAL_LONG(u_countChar32(Z_USTRVAL_PP(str), Z_USTRLEN_PP(str)));
break;
case IS_STRING:
RETVAL_LONG(Z_STRLEN_PP(str));
break;
default:
convert_to_text_ex(str);
RETVAL_LONG(Z_UNILEN_PP(str));
break;
}
}
みたいな感じで、やっぱりUNICODE文字列かどうかで単純に処理を振り分けているみたい。UNICODE文字列とバイナリ文字列が入り交じった状態の比較とか、文字列結合とかがどうなるかまでは追ってない。
2006-08-21 [長年日記]
_ 自転車買った
っつーか、オクサンに2ヶ月遅れの誕生日プレゼントとして買ってもらったんだけど。サギサカのRSXとしか書いてなくて、サギサカってメーカーはWeb上に公式な情報がほとんどないんで、いまいちよくわからん。近所のオリンピックに置いてあった中から機能ベース(前後サスペンション、オートライト、前後泥よけ)で選んだだけなんで。
ちょっとだけ近所をぐるっと回ってきたところ、一応スポーツサイクル向けだけあって、今まで乗ってきた通勤通学自転車と比べると、ずいぶんちゃんと走るなー。車道を走ってもそんなにきつくない。ただ、バックミラーがないと路駐避けとかのときに危ないんで、そのうちつけよう。あと、スピードメーターがついていると思っていたんだけど、二つあるメーターは前後のギア表示用のメーターで、スピードメーターはついてなかった。スピード+トリップメーターも後で買おう。
しばらくは近所を走って体を(特にけつの痛みを)慣らして、そのうちときどき通勤で使えるようになるといいなー。ただ、会社まで25kmもあるから、常時通勤に使うってのは無理だろうけど。時間もどうやったって電車よりかかるはずだし。
2006-08-23 [長年日記]
_ 1470.netメモの一覧表示時の可読性向上
メモを一覧表示する際、メモのパーマリンク表示時以外では、255バイト以上は省略表示していたのを、1000バイトまで省略せずに表示するようにしました。また、省略表示時にも改行は有効になるようにしました。
また、省略表示時に「続きを読む」をクリックすると、従来はメモのパーマリンクに遷移していましたが、Ajaxが有効な環境の場合はその場でコメント全文を表示するように変更しました。
_ String.replace(/\n/g, '<br />')の挙動の違い
↑の2番目の機能を実現するために、Ajaxで取ってきたメモのJSONデータ中に含まれるプレーンテキスト形式のコメント本文を、
comment.escapeHTML().replace(/\n/g, '<br />')
してからinnerHTMLにセットしているんだけど(escapeHTML()はprototype.jsの拡張ね)、FirefoxとOperaでは想定通り表示されるのに、IEではなぜか連続した改行が1個の<br />になってしまい、空行が表現できていない模様。これって何が原因なんだ?
String.escapeHTML()のせいだった
IEでString.escapeHTML()した段階で、連続改行が消えてしまうらしい。確かprototype.jsのesacpeHTML処理ってブラウザ依存の特殊な書き方だった気がするし、そのせいだろうな。
というわけで、自前で置換を使ったescapeHTML()相当の処理に書き直して対応。ただ、非Windows環境ではまた別の問題が出るのかも?
2006-08-25 [長年日記]
_ 残り容量が数十Mバイトになっていた
PCがなんかくそ遅いなーと思ってふと空きディスク容量をみたら、残り数十Mバイトまで減っていた。Folder Size for Windowsで各ディレクトリ単位のディスク使用量をながめてみたところ、
- Thunderbirdでimapでアクセスしているアカウントのデータフォルダに、なぜか1GバイトオーバーのINBOXファイルがあった。なにこれ? 削除したけど別に動作には支障はなし。
- puttyのlogが無限に追記されたよ……。数Gバイト。
- 昔ダウンロードしたCD/DVD-ROMのisoイメージファイルが、そこかしこに消されず残ってたよ。10Gバイトオーバー。
あと、細かいテンポラリディレクトリの中身とか消したら、30Gバイトくらい空いた。そこまでやって久しぶりにデフラグを起動したら、表示が真っ赤(ほとんど全部断片化されている)だったので、最適化実行中。これで使わないファイルの圧縮とかが走ればさらに余裕ができるかな。
_ PHPで安全なセッション管理を実現する方法
なんかこの辺の情報って、ちゃんとまとまっているのを見たことがない気がするんで、まとめておく。特にPHPのセッションを標準設定で使った場合の問題とその対策について。ツッコミ歓迎。
まずセッションの仕組みの基本から。
Webアプリケーションは、通常ステートレス(=状態がない)である。ステートレスというのは何かというと、ユーザー(ブラウザ)が連続的に複数回のアクセス(Webページの表示)しても、サーバー側はそれを特定のユーザーの連続したアクセスと認識せず、単に誰ともしらない複数のユーザーが複数回アクセスしたものとして処理すること。
ステートレスの反対のステートフル(状態を持つ)ってのは、ユーザーがページA→ページB→ページCなんてアクセスをしたり、あるいはページBでフォームからデータを投稿したりしたときに、そのアクセス履歴やら入力したデータなどの情報(=状態)を保持した上で、次にそのユーザーがアクセスしたときに、そういう状態を持つユーザーからのアクセスであることをサーバーが(認識しようと思えば)認識できることを表す。
ステートレスなWebアプリケーションにステートを持たせるための機能が、いわゆるセッション管理機能。これは、
- Webアプリケーションにアクセスするユーザーを特定する
- そのユーザーに関する状態(情報)を、サーバー側で保持する
という二つの仕組みを組み合わせて実現している。
ユーザーを特定するための仕組みとしては、ユーザーに対して自動的に(通常初回アクセス時に)識別コードを割り振り、その識別コードを使って特定する。この識別コードのことをセッションIDという。ユーザー(ブラウザ)は、そのWebアプリケーションにアクセスするたびに毎回必ずセッションIDを送信する必要がある。そうしないと、サーバーは同一のユーザーだと識別できない。
セッションIDは通常Cookieとしてブラウザに記憶され、Webアプリケーション(というか、特定のドメインの特定のパス以下)にアクセスするときには、ブラウザが自動的に送信してくれる。Cookieに対応していないブラウザ(携帯とか)では、あらゆるリンクのQUERY_STRINGにセッションID情報をくっつけて送ったりする場合もある。
このセッションIDというのは、ユニークでありさえすれば機能的(ユーザーの識別)には問題ないんだけど、セキュリティまで考えると、ユニークであればなんでもいいという訳ではない。
たとえばセッションIDを、アクセスしてきた人に連番で割り振るような仕組みになっているとする。自分のセッションIDが100だったときに、試しにセッションIDを99とか98に書き換えてアクセスしてみたら、他人(=自分の直前にそのWebアプリケーションを利用した人)のセッションIDを使ってアクセスできてしまうかもしれない。
その場合、そのセッションIDに結びつけられた情報には、他人の個人情報などが含まれていたりして、そういうものが表示されてしまうかもしれない。あるいは、そういう他人の情報を書き換えてしまったり、あるいはその人の権限で何らかの処理を実行できてしまうかもしれない。
それを考えると、セッションIDは第三者が推測可能な内容であってはいけない。そのため一般的には、ランダム(=ロジックで推測されない)かつ十分に大きい(=可能性のある値を順次試す(=ブルートフォースアタック)ことで、実用的な時間内に正解に行き当たることがない)識別コードを生成して利用する。たとえば、マイクロ秒単位の現在時間+乱数発生器+サーバー固有の値+ハッシュ関数の組み合わせを使って、数十桁の16進数文字列を生成したり、とか。
ともかくそうやってセッションIDを使ってユーザーを識別できるようにしたら、そのセッションIDをキーにサーバー側で情報を保存する場所を用意する。どこに保存してもいいんだけど、PHPのデフォルトのセッション機能では、指定されたディレクトリ(/tmpとか)の下にセッションIDをファイル名の一部に持つファイルを作成し、その中にシリアライズ(バイト列表現に変換)されたPHPの配列を保存するようになっている。セッションハンドラーを変えれば、DBに保存したり、メモリキャッシュに保存したり、いろいろできる。
これで、
- ユーザーがセッションIDをサーバーに送る
- セッションIDにマッチするセッション保存ファイルを読み込む
- セッション保存ファイルの内容を復元(unserialize)して、$_SESSION変数に入れる
といった形でPHPのセッション機能が実現されるようになる。
って、セッションの仕組みの基本を説明しているだけで、ずいぶん長くなったな。ここまでは前振り。次からが本論。
PHPのセッション機能は、セッション固定攻撃(session fixation)に対して脆弱だ。セッション固定攻撃っつーのは、攻撃者が用意したセッションIDを強制的に使わせることによって、本来推測不可能なはずのセッションIDの、推測(というかあらかじめ知ること)を可能にしてしまう方法。
PHPのセッション機能は通常、Cookie経由のセッションIDもQUERY_STRING経由のセッションIDも、どちらも認識できる(session.use_only_cookiesオプションでQUERY_STRING経由のセッションIDを認識しないようにできる)。また、セッションIDが外部から渡された場合、そのセッションIDに対応するセッションデータ(通常はファイル)が存在しない場合は、自動的にそのセッションIDに対応するセッションファイルを生成し、正常にセッションを開始してしまう。
つまり、あるユーザーがあるWebアプリケーションにアクセスする入り口のところで、何らかの方法(Cookieのドメインによる有効範囲をうまく利用したり、セッションID付きリンクを踏ませたり)でセッションIDを固定してしまえば、そのユーザーはそのアプリケーションで、既知のセッションIDを使ってセッションを開始してしまうことになる。
今のところPHPには、セッションデータが生成されていないセッションIDが渡されたときに、そのセッションIDを利用したセッションを有効にさせない、というようなオプションがない(パッチはある)ので、外部から渡されたセッションIDでセッションが開始されてしまうことは避けられない。
じゃあどうすればいいかというと、
- セッション変数の内容を見て、その正当性を確認する
- セッションIDを変更することによって、危険なセッションIDを無効にしてしまう
あたりの組み合わせが対策となる。たとえば、
session_start();
if (!isset($_SESSION['_SESSION_CHECK']) {
session_regenerate_id();
$_SESSION['_SESSION_CHECK'] = array(
'startTime' => time(),
);
}
などとする。
これで、もしも正しく初期化されていないセッションが開始された場合は、session_regenerate_id()関数を使って、新しいセッションIDを生成しなおすことによって、もしかしたら外部から渡されたのかもしれないセッションIDは使わないことになる。
また、次のようにすることで、より安全性を高めることもできる。
session_start();
if (
!isset($_SESSION['_SESSION_CHECK']) ||
($_SESSION['_SESSION_CHECK']['REMOTE_ADDR'] != $_SERVER['REMOTE_ADDR']))
{
session_regenerate_id();
$_SESSION['_SESSION_CHECK'] = array(
'REMOTE_ADDR' => $_SERVER['REMOTE_ADDR'],
'startTime' => time(),
);
}
これは、セッション変数に入っている(=セッション初期化時の)ユーザーのIPアドレスと、今回アクセスしたユーザーのIPアドレスがマッチしない場合も、同様にsession_regenerate_id()関数を使って新しいセッションIDが生成されるようにするコードだ。
ただし、複数のゲートウェイを持つネットワークからのアクセスなどの場合は、正当なユーザーでもアクセスのたびに別のIPアドレスが使われることもあるので、このような書き方はいつでも使えるわけではない。利用するユーザーの環境が限定できる場合のみ利用できる方法となる(あと、リバースプロキシとか使っている環境でもこれだと無意味)。
また、上記のような特定の(不正な可能性のある)条件にマッチした場合に限らず、たとえば、
session_start();
if (!isset($_SESSION['_SESSION_CHECK']) || (rand(0, 100) > 99)) {
session_regenerate_id();
$_SESSION['_SESSION_CHECK'] = array(
'startTime' => time(),
);
}
のように、ランダムに1%の確率でセッションIDの変更を行うという方法もありだろう。これによって、未知の(準備したロジックでは判別できない)攻撃に対しても、ある程度安全性を高めることができるようになる。
しかし、実際には上記のコードはあまり役に立たない。
というのは、session_regenerate_id()関数は、セッションIDは付け替えるが、古いセッションIDに結びつけられたセッションデータ自体はそのまま残してしまう(PHP 5.1.0以降ならば、session_regenerate_id(true)とすることで、古いセッションIDに結びつけられたデータを破棄してくれるので、以下の話は関係なくなる)。たとえば、元のセッションIDがxxxxxxで、session_regenerate_id()後のセッションIDがyyyyyyyだった場合、セッションデータファイルとしてはその両方が残ってしまう。
そのため、せっかくセッションIDを変更しても、古いセッションデータに有効な情報(たとえばあるユーザーのログイン権限と結びつけられた情報など)が含まれていた場合、せっかくセッションIDを付け替えても、古いセッションIDの方が悪用されてしまう可能性がある。
単純に、session_regenerate_id()でセッションIDを変更するだけで安全性が確保できるのは、あくまでもsession_regenerate_id()する前のセッションデータに、有効な情報が含まれていなかった場合のみなのだ。
そこで、
if (isLoginOk()) { // 認証が通った
session_regenerate_id();
$_SESSION['userId'] = [ユーザーID];
}
のような形で、ログイン処理の後など、セッション内に重要なデータを登録するタイミングで、session_regenerate_id()を実行してセッションIDを付け替えることによって、セッションの安全性を高めることになる。
たとえそれまで使っていたセッションIDが危険なものだったとしても、それには重要な情報は含まれておらず、重要な情報は必ずサーバー側で新しく生成したセッションIDに結びつけられることになるからだ。
しかし、未知の危険性に対策のために、すでにセッションデータに有効な情報が含まれている状態で、セッションIDを変更したい場合もあるだろう。その場合はどうすればいいだろうか。
セッションを継続する必要がないのならば、session_destroy()を実行することで、現在のセッションIDに結びつけられたセッションデータは破棄される。しかし、それでは新しいセッションIDで今までの情報を引き継ぐことができない。
そこで、次のようなコードを使うことになる。
session_start(); $tmp = $_SESSION; session_destroy(); session_id(md5(uniqid(rand(), true))); session_start(); $_SESSION = $tmp;
テンポラリ変数に現在のセッション変数を待避してから、現在のセッションデータを破棄し、新しいセッションIDをsession_regenerate_id()を使わずに独自のコードで生成してから、再びセッションを開始し、先ほどテンポラリ変数に待避してあったセッション変数を、新しいセッション変数にセットし直す、という方法だ(なぜsession_regenerate_id()ではなくsession_id(md5(uniqid(rand(), true)))を使っているかについてはコメント欄参照)。
これで、すでにセッションデータに有効な情報が含まれている場合でも、セッションIDを変更して安全性を高めることができることになる。
何か問題がありそうな記述があったらツッコミください。修正しますんで。
2006/11/20 http://tdiary.ishinao.net/20061120.html#p01に追加情報を書きました
2006-08-29 [長年日記]
_ またマルチアカウントなspammerが来てるなー
xdjfgbuhg vburhnem y564jf8mg m3nf72b6 x6j37fhga ldi9ebguf cgytxu3y91 wyebfts3 p4n56122 f6hfuye6 a4hr63g2a fjf74fhd62 hhdyn36 v4hte53hd dkduen7g dkduen3ygt jduye73bg ikkifjeu3 due7e63h9 y93je7fw jsyd74nbg cvcxrdrk4 pdur73b1 shs71h36f aaudyrng fjryu4hgoo lseujg84 skisu47gh xcefry391 s7sbh37gh dkdugu4ng duu3bgpg hmbnvye43 dud71s9d sis8gj00j djr74ngidy p4kgu7gber x7rh4ht6 nsy8fjt3e firjtu36 irjtufjr74 suryru74 dkdue732a vkrirh47 soed74se lkgjfnruv n47rhw93 p12ns7fe du37eu zbceu39 g4n5r7rh1 ir74nhft35 ogjrnfyu djyu37gfe so4tyr632 f2314det aofjrur73h fjfysbsfq i3he51f dhe351hs lkgurndg vncmdh3 h4nd63n27 o4f6ebwy sksye63bg ffye73bg kis3hgy73 z9f843j gkfj4gh98 f9f84jh7 d8485jgb apf93jes d9h4jh84h d9h84gg5 d74j0d84j dk3udj37 mvlwp9jg dlod94jh9 dod84jtff d47j8ed2 ptihjruf fgifdnrf8 r4t7461 a0z38gk e47tfjri sv84jf75 zlfi4jgy prltmfjs jfu5ke84 xmbjfnri sk3ls81j y47fj5uro dlritj4nbh kkdirj48 jfmtt273 plfjrntyu n5hyrf74t tt84jt849s4 c02f450t woitr74n9r lorgor834 orm9tr4n4 lainj1349 mie8t02f7 pa9ktcmr7 shrbur348j inr4jr8t34 aru94giuu z244838en ma83n8eer an4hnq wer8932 owkre934r4 bnur734r7 kfoe8u343 ojf9w4t7 mc48n5t85 o4u45h4t5 nvty5vb5wp wovu8544jr iwpvu857 ju58w0vw vktu58w mv587tw8v signr74j5 gkvirng8 so48jg8 pwirnbi7 afkjugt84 mf4mf84 g84ns8t l8fm48a n77a4jr9 nv4ka94l8 w83940t p93ja8aa ms94ng94 l884ma4 krkan93 lar8an34r a0023r4b oi38r8gc anfiarar3 mvosiru9a oiur9vfrf rryr83n8a aoinar8 vkr8a46a wfiejroaer7 m83n8rn8r kdfjae84 lkdjgsproi kd83jnga9 kfmru33 nvutks7e nvwpf6b pmxurai ma38q4js zqjldf9ua oa8ka349rk bndkeyr6 mce8eu kurjr7ea krfa8ra la3rjfna aeiefhai7 nfloeua7 oqm7f4kg8a v72mvks nbbcye363 zarq03q zlaia23r ashape3 zmar131 apaura098 vkoar9a3 arakea8 kru63nf7 nciaor81 xveyq734q aeiapr7a bspiru8 jgp49jd fjbg3i fjgblwlp3 gl3oyfks gugj4649 gggfjggk nfl02840 kawausa jrh84839 lhgjro49 jgle6389 zmzcmcor2 mrhyfk820 aieial78 ieje745s amaoei3 lvue7rtr ja38jcfie7 e8fe73fre eue7jee3 cmdua759 e3jfaviez zoe74tf zndgt258 cbsger8r cmfi73ak mchr629 ovneyxmg laien6394 m81kfur7 hfirg3p9 jglfidge62 hfltgoege0 hekrhd6 h94jgklfhw j5b2740rkf pp4jgjdjfj hoglnvbcm ncvbmrju bcieibup3 vbcx840 bhgbjnc93 bmchv3902 nvbwfhi6 bcmo38a jflgh079jdl ofklsb6 nbk730hr20 kfjbwmdj0 ghdlkht9 nhflir0 b639flwh i8394heiii kdj26r983 kdoud993 hlriyt89 jfrketl2g7 kflypfwo93 lkhkglwk ldfor0legk krhebi7f hwu042003 hfi6tfh wrhfl38 hgkwugtl8 rkhw73 lhbwlfu34 jfrjglw7 hkrugo48 j4hlkwlt jhg37tlw jkegflw94 lrfhtp3r59 it89tg1tlw hlbicge3 ker3lg83h hfkrgh0 jfoehjf20 hek39fl isjdkkif3 h83y3r xiu94575 e23i84her egtp2i4 hgo300200 jf73pgj21 gh2906sji hfo39t hi3y693k hfi39t2k lejp4ug0 gkp3ut9 pfjgp37lwsp lkpjvgii8 kpehs605 kgoe305jg h495kd3o oghdo93 loehf972 loh04jhpe jhoehgi4o rhornfl8 jpemhjru korkho47 fhofki47k3 jpyti7639t joehdmn83 khrofnr9 heoemj83ki djvo370fh jfpwgfk6k hfwkdjkv2 tttflehwo jgprhfpo370 yeorjf73 kgoirhdkd6 jroeheo9 fjfrigh8
前回の情報とあわせて、spammerがどういうことをやっている&やりたいのか分析する対象として使わせていただきます。飽きたらアカウントごと消します。
それにしても
前回も今回も、基本的に1アカウントごとに専用ドメインを持つ1サイトのみを登録していってるけど、最近ドメインが安くなっているとはいえ、それなりに投資はしているんだろうなー。それともどこかにただ同然でドメイン+レンタルスペースを配っているサービスとかがあって、そういうのを使っているんだろうか? 内容は良くありがちなキーワードポータル系で、できはいい方ではないけど、こんなんでもそれなりの成果はあるのかなー?
_ Plaggerをインストールしてみた
死ぬほど依存モジュールが多かったけど、基本的にCPANでinstall Plaggerときどきforceな感じでインストールできた。で、よく分からんけどまず基本はBloglinesのGmail転送だよねと、YAMLファイルを書いて、plagger --config bloglines2gmail.ymlとかしてみたら、gmail_notify.ttがないとかエラー終了して、Bloglinesの未読が消えた。がっでむ。結局、
svn export http://svn.bulknews.net/repos/plagger/trunk/plagger/assets/
でテンプレートファイルをレポジトリから持ってきて、global/assets_pathにそのディレクトリを指定して対応したけど、テンプレートファイルってこうやって別に入れるものなの? どこかにインストールされていそうな気がするんだけど、よくわからん。
2006-08-31 [長年日記]
_ サーバートラブル中です
セッション&キャッシュサーバーでディスクフルをやらかしてしまいまして、1470.net関連のサービスが不安定になっています。
セッション&キャッシュを試行錯誤(ファイル、memcached)の末、MySQLバックエンドに切り替えたんだけど、セッション&キャッシュ用MySQLでbinlogを取る設定になっていたんで、セッション&キャッシュの大量の更新ログを出力してしまい、想定外の速さでディスクを食いつぶしていった、ということだった。
_ いろいろ買った
近所のオリンピックでテキトーに自転車を買った後に自転車ツーキニスト(疋田 智)を読んだら、「ちゃんと自転車に乗る気があるなら、近所のスーパーで自転車を買ってはダメ」と書かれている(本格的な利用に耐えられる自転車はスーパーとかでは売ってない4、5万円〜の価格帯のやつだそうな)のを読んで、orz...となったわけですが、たぶんいい自転車と乗り比べないと俺には違いはわからないだろうということで、しばらく体が慣れるまではこの自転車に乗っておこうと思いつつ、いろいろ関連物を買ってみたわけですよ。
今日買ったのは、ドリンクホルダーとサイクルコンピュータ(キャットアイ サイクルコンピューター CC-MC100W マイクロワイヤレス)とグローブとサドルバッグとバックミラーとオイルと空気入れ。一通り取り付けてみたんだけど、サイクルコンピュータがセンサーから情報を受け取れていない。センサーと磁石の距離が遠すぎたのかなー。それともセンサーの故障? といったところで今日は時間切れになってしまったんで、明日続きをやらないと。これが一番楽しそうなブツなんだから、ちゃんと動いてくれないと困るのよ。



_ takayama [IEの場合は.replace(/\r\n/g, '<br />')とかやるとうまくいかないですか? 前に同じ事ではま..]
_ ishinao [本文の方に追記しましたが、prototype.jsで拡張されたString.escapeHTML()の段階で、連続改..]