(個人ブログなので「携帯電話個体識別番号」ではなく「端末固有 ID」と呼ぶよ)
ということで、
- OpenPNE 3.5.3 でおこなった「かんたんログイン」の変更について
- http://www.openpne.jp/archives/5070/
の件です。
ここで説明されているのは(技術者には既知である事項の説明を除くと)、要するに以下のようなことです。
- Cookie 内のランダムな ID を見る認証手段を用意したよ
- Cookie が使える端末では端末固有 ID ではなく Cookie 内の ID を見て「かんたんログイン」するようにしたよ
- 認証手段を「A: Cookie 内 ID のみ」「C: 端末固有 ID のみ」「B: A と B を併用」できるようにしたよ(see: http://twitter.com/co3k/status/15973375832 )
- B の方法を選んだ場合は、ユーザが Cookie 内 ID の利用を開始していないか、そもそも Cookie を利用できる端末でない場合は端末固有 ID で認証するけど、ユーザが Cookie 内 ID を利用しているのに Cookie にその値がなく端末固有 ID での認証を使用としてきたら弾くよ
……ということで、まあ、特に難しいこともなく、これだけなんです。
実装内容については、実際の差分を見ていただければわかるのかな、と思います(何か疑問点などあればお答えします)。
- 登録時の Cookie 内 ID の付与
- http://github.com/openpne/OpenPNE3/commit/b1bd42e951adb4cb8b66865257fc5949f3fbd267
- 認証部分 (opAuthMobileUIDPlugin)
- http://github.com/ebihara/opAuthMobileUIDPlugin/commit/83896d9e6318208cb51b175e0ed1f9e1f5a18089
- Cookie 内 ID の更新
- http://github.com/openpne/OpenPNE3/commit/bd6c562674e15b645165bd90b334e6fbd1d504e5
ただ、この機能を実現するにあたってそれなりに苦労した点があるので、ここではそのことについて書こうと思います。
SSL 時の Cookie 引き継ぎの問題
(Twitter 上で高木浩光さんから http://twitter.com/HiromitsuTakagi/status/16057716034 という指摘を受けているので、ここで紹介している secure.softbank.ne.jp にまとめる方法について問題ないかどうか、ソフトバンクモバイルに問い合わせしてみようと思ってます。あくまで以下は「OpenPNE 3.5.3 ではこうやっている」という程度の情報と思ってください)
これはどちらかというと、端末固有 ID 認証というより、 au や SoftBank 端末でセッション継続用 Cookie が SSL と非 SSL 間で維持できないことに関する対策なのですが……
SoftBank での HTTPS 通信には secure.softbank.ne.jp を通すパターンとそうでないパターンの 2 種類があり得ます(HTTP のコンテンツ経由で HTTPS のコンテンツに遷移した場合は secure.softbank.ne.jp を通るが、メール内 URL などから直接 HTTPS のコンテンツにアクセスした場合は secure.softbank.ne.jp を通さない)。 secure.softbank.ne.jp を通す場合と通さない場合で Cookie をどう格納するかが変わるので、 SoftBank で Cookie を利用する場合はこの点に気をつける必要があります。
- OpenPNE では secure.softbank.ne.jp 対策として、以下の対応をおこないました。(対応チケット自体は http://redmine.openpne.jp/issues/1112 です)
- http://github.com/openpne/OpenPNE3/commit/46d87866bb5eb049caa30cb2d7198d4216bc77de
SoftBank 端末の HTTPS 通信は secure.softbank.ne.jp 経由に限定し、通常の HTTPS 通信でやってきた場合は secure.softbank.ne.jp 経由のアクセスになるようリダイレクトするというものです。 ただ、ここで問題になるのが、「現在のリクエストが secure.softbank.ne.jp を経由しているのかどうかアプリ側から知ることができない」という点です。そこで、ここでは、
- X-JPhone-UID はゲートウェイで付与されるので、リクエストにこれを含む HTTPS 通信は secure.softbank.ne.jp を経由していると見なす
- X-JPhone-UID が取れない場合は secure.softbank.ne.jp 付きの URL にリダイレクトする(X-JPhone-UID を送信していないユーザの場合にリダイレクトループするのを防ぐため、リダイレクト前にセッションクッキーを発行し、このクッキーを持っている場合はリダイレクトしないようにする(つまり、 secure.softbank.ne.jp へのリダイレクトは一回しか行わない))
という、「普通に使っているユーザであればなんとか問題なさそう」というのが最低限保証できるぐらいのレベルの対応はおこないました。
認証に使う Cookie 内の ID の生成について
PHP の srand() 関数は Mersenne Twister のアルゴリズムを使っているようです(http://jp2.php.net/manual/ja/function.mt-rand.php)。これは予測可能な乱数を生成するので、これだけではこの用途の ID の生成にはさすがに使えません。
- かといって初回のセッション ID を使い回すのもどうか……と悩みに悩んで見つけたのが、 Ethna の Ethna_Util::getRandom() です。
- http://git.sourceforge.jp/view?p=ethna/ethna.git;a=blob;f=class/Ethna_Util.php;h=c3ab01a60f97e667ded1b13a6cdf8faea9991e9a;hb=HEAD
Ethna_Util::getRandom() ではまず mt_srand() により、 microtime() の結果に getmypid() の結果を加算したものを、乱数生成器にシードとして指定します。その後、 /proc/net/dev が読める環境であればこの中身と mt_rand() と getmypid() の結果を結合した文字列と、現在時刻と mt_rand() により生成した値の MD5 ハッシュを生成します。
で、 Ethna は BSD License だったので、 Ethna のコードの一部を Apache License な OpenPNE で流用することはライセンス的に矛盾しません。そういうわけでこの Ethna_Util::getRandom() をありがたく拝借し、 opToolkit::getRandom() として利用させてもらうことにしました(http://github.com/openpne/OpenPNE3/commit/b1bd42e951adb4cb8b66865257fc5949f3fbd267#diff-5)。ありがたいかぎりです。
ということで認証に使う Cookie 内の ID の生成には、 opToolkit::getRandom() (Ethna_Util::getRandom())を使っています。前置き長っ。
# ところで Ethna では PHP が生成するセッション ID ではなく、このメソッドで得られる値をセッション ID として使用しているようです(ちゃんとコード追っていないんで間違っていたらごめんなさい)。
メンバーと紐づけた端末固有 ID 利用の完全な脱却について
結局これは今回は断念したんですが…… OpenPNE で使っている「ブラックリスト」機能は、ログインしようとしている端末の固有 ID が、 DB 内の禁止するべき端末固有 ID のリストに含まれている場合はログインを弾くというものなので、 OpenPNE 側としては他の情報と一切紐付かない、ただの端末固有 ID のリストがあればそれでよいわけです。
問題なのが管理画面から特定のユーザの端末固有 ID をブロックしたい場合で、この機能をメンバーと端末固有 ID とを紐づけずにどうやって実現するかどうか、とかまで考えたかったのですが、残念ながらそこまでの余裕がなかったので、次の 3.8 に持ち越しの課題にしようと思っています。
一応の案としては「ブラックリストに入れたいメンバー ID リスト」みたいなものをもっておいて、そこにリストアップされたメンバーがログインしようとしたときに、そのメンバーの端末固有 ID をブラックリストに入れる、という感じのものがあるといえばありますが、どうなんだろうなあ。
とにかく「ブラックリスト」機能がメンバーと端末固有 ID を紐づけずに実現できるのであれば、あとは i モードブラウザ 1.0 が絶滅するのを待てばよいと……。その日が訪れるのが今から楽しみですね!