T

Lokka

WordPressを憎む根性曲がりな人のための密やかな楽しみ、Lokkaをいじってみた。というかgithubにあるので華麗にフォークした。

ざっと眺めて、最初に必要なのはプラグインの自動ロードだろうなあ、と思っていたのだけれど、迷うところが2点。ひとつは、まあルールにしてしまえばいいんだけど、自動ロードするからには何らかの命名規則に従ってもらう必要がある。ちょうどサンプルにhelloプラグインがあったので、これ幸いと

plugin/hoge/lib/lokka/hoge.rbがあったらHogeクラスをregisterする

ということにしてしまった。これはSinatraでいうところのregister Hogeと同等なのだが、じゃあSinatra拡張モジュールの二大巨頭であるところのhelpersはどうするんだという問題がある。するとHelperの方を拡張するやつを書いている人が見つかったので、

plugin/hoge/lib/lokka/hoge.rbの中でHelperを再オープンしてくれたら活きになる

ということにした。それでhelpers Lokka.Helpersが呼び出されるより前にLokka.Helpersを拡張できるようにしておけばいいじゃない、ということでさっそく変更を施してコミットしておいた。呼び出される順番ってのもあるわけだが、それはRuby側の問題だってことにしておけばいいじゃないか、と心の中の悪魔が言うので従っておいた。以上がプラグイン作成のお作法、英語でいうところのconvention over configurationである。

ちょっと仕事が狂ったように忙しいので、ほとぼりが冷めたらプッシュしておこう。

参考文献:Pyhaの大体の仕組み

update: マージされたよ!これでみんなAkismetとAll in one SEO packとHowdyを作ればいいよ!

iPhoneのカメラでプライバシー情報ダダ漏れだった

iPhoneで撮影した画像(おもに赤ん坊)をここにたくさん掲載していたが、同僚がExif ViewerというFirefoxのプラグインを見つけて画像情報を見たら、撮影場所の位置情報が見れたというので驚いた。スクリーンショットを保存したつもりができていなかったようで見せられないが、GoogleMapにリンクまで作成してくれるので見事に自宅の場所までわかる。

iPhoneの仕様ではメールでの送信などではGPS情報を削除しているようだが、iPhotoで同期した画像からは消えないので、そのままブログに掲載すると予期していない情報を公開してしまう可能性がある。

というわけで、jheadを使ってデータを修正した。

#!/bin/sh
FIND=/usr/bin/find
EXIFTOOL=/usr/bin/exiftool
JHEAD=/usr/bin/jhead
PATH=$1

$FIND $PATH -type f  | while read FILE
do 
        RESULT=`$EXIFTOOL -gps:GPSLatitude $FILE`
        if test -n "$RESULT"
        then
                $JHEAD -purejpg $FILE
                echo $FILE
        fi
done

WordPressのアップロード用ディレクトリにある画像ファイルに一括でjhead -purejpg path/to/file.jpgを実行してExifデータを削除しておけば間違いはない。元の画像は別にあるので問題ないだろう。今後もアップロードしてしまうことがあるだろうから、とりあえずcronでチェックすることにする。

iPhone用プラグインWPTouch

iPhoneを見せびらかしに来た人物がWordPress用プラグインの出来がいいというので試してみたのだが。

一覧ページ:
photo2

抜粋だけ見る:
photo1

エントリの詳細:
photo

出来がいいので腹が立つ。iPhoneを横にすると画像がリサイズされる。

しかも、テンプレートに埋め込んだJavascriptで強制的に回転させる技が通じない。

塩引鮭がやってきた

およそ二週間ぶりに休めた日曜日の朝、突然宅配便がやって来た。紺色の長細い箱に入っていたのは、新潟県村上市の「塩引鮭」。痴れ者からのお歳暮だ。

081214_194402

ちゃんと家族のみんなになじんでくれるかな?ちょっと顔がこわいけど。

081214_1941011
上の子とはすぐに仲良くなりました。

081214_194001
二人は親友。

081214_194301
最初は警戒していた猫。

081215_000103
塩引鮭の方から近寄っていくと…

081214_235902
一緒に猫のおもちゃで遊んでいます。

081214_235901
すっかり仲良し。

081214_235802
でも塩引鮭ってちょっと凶暴なところがあります。

新しい家族が増えてみんなが喜んでいると、突然異変が起こりました。今日はみんながあんまりかまってくれないから、すねてしまってのでしょうか。まめぞうがぴょんと立ち上がろうとすると、なんと

081214_194901
しっぽが生えているではありませんか!

081214_194801
「まめ、宗介、嫌い!」

さて、ちょっとした騒ぎになりましたが、慣れない長旅で疲れた塩引鮭もそろそろおやすみの時間です。

081215_000801
ゆっくりお休み。

081215_000902
こんな怖い顔なのに、家族の寝顔と思ったらなんだかかわいく見えてきてしまいました。

081215_000901
一緒に寝よう!

081214_235001
布団をめくると、あれ?

というわけで、短い間でしたが、新しい家族と過ごせて幸せでした。あと、けっこうしょぱいのですがご飯に合うので朝は茶漬けにするといいですね。

081214_200501
三枚に下ろすのは難しいのでこうやってぶつ切りにするといいみたい。

081214_195201
ええい、トラウト・マスク・レプリカ!

081214_195301
なにすんだよぅ!

081214_235101
興味しんしん。

WordPress2.7

毎日バックアップを取っているので気楽に更新してみた。

WordPress更新

2.6.5が出ていたので更新。主な変更点はIPで振り分けるVirtualHostの設定になっているApache2系のサーバでXSS攻撃が可能になる問題の修正。

ここはwp-includes/feed.phpとwp-includes/version.phpを新しくするだけで更新できる。2.6.4は飛ばされて2.6.5がリリースされたことになる。ちなみに2.6.4は今後も絶対リリースされないとのこと。

WordPress更新

変更があったのは以下2つのみ。

1. wp-includes/class-snoopy.php
2. wp-includes/version.php

管理画面の下の方に出ているフィードの取得部分に使われているSnoopyライブラリの脆弱性に対応とのこと。

MySQLのカラム複製脆弱性

昨日書いたMySQLの脆弱性ですが、手元の環境で見事に再現しました。MySQLのバージョンはCentOSでyumからインストールしたmysql-4.1.20-3.RHEL4.1.el4_6.1です。

手順は以下の通り。まずデータベース、テーブルを作成します。

mysql> create database sec_test;
Query OK, 1 row affected (0.02 sec)

mysql> use sec_test
Database changed
mysql> create table test (username char(16));
Query OK, 0 rows affected (0.01 sec)

ご覧の通り、char(16)でusernameというカラムを持つテーブルを作成します。

まず「admin」というusernameを持つ行を作ります。

mysql> insert into test (username) values ('admin');
Query OK, 1 row affected (0.00 sec)

mysql> select count(*) from test where username = 'admin';
+----------+
| count(*) |
+----------+
|        1 |
+----------+
1 row in set (0.00 sec)

出来ています。

次に、問い合わせの文字列部分の後ろに空白を追加してみます。全部で16文字になるようにしました。

mysql> select count(*) from test where username = 'admin           ';
+----------+
| count(*) |
+----------+
|        1 |
+----------+
1 row in set (0.00 sec)

後ろの空白が無視されているのがわかります。update:正確にいえば、char型なので指定された長さ未満の文字列は空白文字で埋められるみたいです。null文字とかじゃないんですね。

次に、char(16)の範囲を超えたところで「x」を追加して検索してみます。

mysql> select count(*) from test where username = 'admin           x';
+----------+
| count(*) |
+----------+
|        0 |
+----------+
1 row in set (0.00 sec)

さすがにヒットしませんでしたが、そもそもカラムの制限長を超えているのにエラーにはなりません。

では、この値をINSERTしてみます。

mysql> insert into test (username) values ('admin           x');
Query OK, 1 row affected, 1 warning (0.01 sec)

mysql> select count(*) from test where username = 'admin';
+----------+
| count(*) |
+----------+
|        2 |
+----------+
1 row in set (0.01 sec)

「admin」としてINSERT出来てしまいました

これで、(1)既存のデータに重複行があるかチェック(2)なければINSERT、という処理で重複登録を防ごうとしてもダメなことがわかります。

ちなみにPostgreSQL 7.4.19でテストすると

db_test=# create table security_test (username char(16));
CREATE TABLE
db_test=# INSERT INTO security_test (username) VALUES ('admin');
INSERT 0 1
db_test=# SELECT COUNT(*) FROM security_test WHERE username = 'admin';
 count 
-------
     1
(1 row)

db_test=# SELECT COUNT(*) FROM security_test WHERE username = 'admin           ';
 count 
-------
     1
(1 row)

db_test=# SELECT COUNT(*) FROM security_test WHERE username = 'admin           x';
 count 
-------
     0
(1 row)

db_test=# SELECT COUNT(*) FROM security_test WHERE username = 'admin            ';
 count 
-------
     1
(1 row)

db_test=# INSERT INTO security_test (username) VALUES ('admin           x');
ERROR:  value too long for type character(16)
db_test=# SELECT COUNT(*) FROM security_test WHERE username = 'admin';
 count 
-------
     1
(1 row)

db_test=# 

INSERTはできません。8.3.3でも。

db_test=# create table test (username char(16));
CREATE TABLE
db_test=# INSERT INTO test (username) VALUES ('admin');
INSERT 0 1
db_test=# SELECT count(*) FROM test WHERE username = 'admin';
 count 
-------
     1
(1 row)

db_test=# SELECT count(*) FROM test WHERE username = 'admin           ';
 count 
-------
     1
(1 row)

db_test=# SELECT count(*) FROM test WHERE username = 'admin           x';
 count 
-------
     0
(1 row)

db_test=# SELECT count(*) FROM test WHERE username = 'admin            ';
 count 
-------
     1
(1 row)

db_test=# INSERT INTO test (username) VALUES ('admin           x');
ERROR:  value too long for type character(16)

INSERT時にあふれた分が勝手にカットされることはありませんでした。

WordPress 2.6.2はセキュリティ修正

WordPress2.6.2が出たのでさっそく更新したが、今回のリリースにはセキュリティ関連の修正が入っていた。

1 推測されやすい乱数生成の問題

PHPのrand関数はlibcのrandのラッパで、mt_randはメルセンヌ・ツイスタ擬似乱数生成装置の実装なのだが、どちらも32ビット型符号なし整数をシードにしている。しかし、実装の問題により実際にはそれ以下の強度にしかならないそうだ。そのため、暗号化に際してこれらの関数を利用するのは不適切ということになる。それに対処したようだ。

2 MySQLのクエリの長さ制限の問題

MySQLではデフォルトでサーバとクライアント間で送信されるクエリとその応答の長さがmax_packet_sizeディレクティブ(っていうのかな?それはApacheだけ?)で1MBに制限されている。もし1MBを超えたクエリがクライアントから送信された場合、MySQLサーバ側でエラーとして処理してしまう。

たとえば、期限の切れたセッションデータをDBから削除するようなクエリがあり、意図的にこのクエリが長くなるようなデータをセッションに埋め込むことができれば、クエリはいつまでも実行されず、何かしら問題が起きることになる。

update: もっとすごい問題があった。

3 MySQLのダメダメな文字列比較ルール

MySQLにこんなクエリを投げる。

SELECT * FROM user WHERE username = 'admin';

たとえば、ユーザ登録の際に登録名の重複チェックをしているとしよう。WordPressのように、そのサイトにはadminという名のユーザが必ず存在するとして考える。新規のユーザが登録時に「admin」という名前を使おうとすると、上のようなクエリでチェックが行われ、すでに登録されている名前であるとしてアプリケーションによって登録がはじかれる。まあ、そういう仕組みのウェブアプリがあるものと思ってもらいたい。

このusernameというカラムが、たとえばchar(16)だったとする。MySQLではSELECTによる比較時にクエリ内の文字列の後ろの空白は無視されるようになっている。「admin           」だと、

SELECT * FROM user WHERE username = 'admin           ';

こうなるが、実際には最初のクエリと同じ扱いになる。

ところで、じゃあ「admin           x」という名前を使おうとしたらどうなるだろう。ここでは意図的に名前をchar(16)から外れるようにした。数えにくいかもしれないが、17文字ある。

SELECT * FROM user WHERE username = 'admin           x';

MySQLでは、比較の際にはカラムの型を無視してくれるので、上のSQLは何も返ってこない。つまり、新規登録可能なユーザ名として認識される。そこで、INSERET処理を実行すると、今度はカラムの型をみて余分なところはカットし、さらに後ろの空白を取り除くので、あれま、「admin」という名前でユーザ登録が完了してしまう。

これでadminアカウントの乗っ取りが完了する。ひええ。

もちろん、UNIQUE INDEXで対処することは可能だが、そうでないアプリケーションなら困ったことになる。

未検証なのでなんともいえないが、本当だったらこわい。

update:再現しますた

アレックス

息子の保育園の同級生に、アレックスという子がいた。太っちょで元気な、甘たれでやさしい子だった。かみさんにもよくなついていたし、息子とも仲良くしてくれていた。

次男が生まれて名前を考えていたとき、アレックスのことを思い出した。アレックスっていい名前だよな。王の名前だもん。しかもただの王じゃない。アレキサンダー大王は東欧からインドの手前までを版図とする大帝国を築き上げ、インドの手前で引き返したのが今でも謎といわれるくらいのとんでもない大王で、ずっと後世までその名を冠した都市アレクサンドリアはローマ帝国が燃やすまで巨大な図書館があったりしてずっと栄えていたし、家庭教師ときたらなんとアリストテレスだもん。

町山智浩の「ツォツィ」の映画評でずっと心に残っていた一言がある。

何の希望もないツォッツィはその赤ん坊に自分の本当の名前をつける。

 デイヴィッド。

 今はクズ同然の彼も、王の名を持って生まれてきたのだ。

こうして取り出してみると、本当になんでもないような言葉かもしれないが、これを読んだときから、なぜかずっと心に引っかかっていた。息子に王の名前をつけるって、どういうことなんだろう、とか。

アレックスも、日本人ばっかりの普通の小学校に進学したと思うから、きっと今頃は周囲の子たちと違うってことでいろいろ苦労しているんだろうな。でも、お前の名前は大王の名前だし、他にもアレックス・ラミレスとかアレックス・カブレラとかアレックス・ロドリゲスとか野球のスーパースターたちと同じ名前でもあるんだから、そんなのに負けないでがんばれといいたい。

だいたい、コスモポリタンって言葉もアレクサンダー大王時代に成立した概念だったはずだ。アレクサンダーの巨大な帝国が、当時、ヨーロッパと地中海沿岸地方と小アジア、西アジア、南アジアに至るまでの広い地域が突然ひとつの国となって交流が盛んになり、民族とか人種という小さな枠では収まりきらない新たな世界観を人間が手にする契機になったという話だったとはずだが、それを考えると、やっぱりまわりの連中とちょっとばかし違うなんてどうでもいいことは吹き飛ばして元気にやっていてくれたらいいなと切に思う。