CakePHP2.4からAuthComponentのパスワードのハッシュ化の扱いが仕様変更され、デフォルトではSimplePasswordHasherというクラスを用いてハッシュ化されるらしいので、従来のハッシュ値と変わってしまってログインできないというハマリどころに数日悩まされた、あとuserModelに騙されて

CakePHP2系はずっと使っていて、Authコンポーネント(AuthComponent)も使って認証画面を実装することも、ままあった。今回またCakePHPを使って認証画面の実装を行おうとしたら、なぜか認証がいくらやってもうまく通らない。パスワードは事前にハッシュ化してテーブルにINSERTしているし、カラム名の指定も間違ってない。不安になりながら過去成功していたソースと見比べてみても、まったく同じように実装している。なのに認証が通らない。アカウントがあることは認識できているのでデータベースの設定などの問題でもテーブル構成の問題でもない。となると一体何なんだろうか……悩みぬいて数日(いや1週間ぐらいか)、ハマッたのである。あとuserModelオプションにはすっかり騙された。


結論が知りたい人もいるだろうから結論から先に書くと、CakePHP2.4からAuthComponentでのパスワードハッシュの取り扱いに仕様変更が起きていたということだ。2.4のリリースノートを見ると「Password hashing has been extracted into a set of PasswordHasher classes. These classes make changing out password hashing strategies for the various authentication adapters simple.(誤訳可能性有:パスワードハッシュ化はPasswordHasherクラスのセットの1つに外出しされた。複数の認証を容易に適合させるために、これらのクラスはパスワードハッシュ化手順の変更を行った。)」と書かれている。複数あるハッシュアルゴリズムのどれを選んでも実装上大きな改修などを必要とせずシンプルに取り扱えるようにするため、といったところだと思われるが、後方互換性がなくなる改修をされると、かなりあせる。
なおタイトル末尾の「userModelに騙されて」についても後述。

■Release CakePHP 2.4.0 is ready
https://github.com/cakephp/cakephp/releases/tag/2.4.0

パスワード発射ー、じゃなくてパスワードハッシャーの話はとりあえず後回しにして、実際に問題なく実装したコードから先に展開してみたい。まずAppController.phpには、以下のようにSimplePasswordHasherクラスの読み込み先がどこであるかをApp::uses()メソッドで指定しておく。

[php]
App::uses(‘SimplePasswordHasher’, ‘Controller/Component/Auth’);
[/php]

次にAuthコンポーネントの設定を$componentsプロパティで以下のように指定する。Session、RequestHandler、Cookieは必要に応じて。少なくともログイン処理でsetFlash()メソッドなどを使う場合にはSessionコンポーネントの利用は不可欠だろう。

[php]
public $components = array(
‘Session’,
‘RequestHandler’,
‘Cookie’,
‘Auth’ => array(
‘loginAction’ => array(‘controller’ => ‘admins’, ‘action’ => ‘login’),
‘logoutAction’ => array(‘controller’ => ‘admins’, ‘action’ => ‘logout’),
‘loginRedirect’ => array(‘controller’ => ‘admins’, ‘action’ => ‘manage_top’),
‘authError’ => ‘ログインしてください.’,
‘loginError’ => ‘ユーザー名もしくはパスワードが違います.’,
‘authenticate’ => array(
‘Form’ => array(
‘userModel’ => ‘Admins’, // 「userModelに騙されて」
‘scope’ => array(‘Admins.status’ => 1),
‘fields’ => array(
‘username’ => ‘username’,
‘password’ => ‘password’,
‘status’ => ‘status’,
),
/*
‘passwordHasher’ => array(
‘className’ => ‘Simple’,
‘hashType’ => ‘sha256’,
),
*/
),
),
),
);
[/php]

13行目のuserModel名にはモデル名を指定する。一応コメントに、ブログタイトル末尾の「userModelに騙されて」と書いておいたが、ドキュメントを読む限り、モデル名は複数形をとらない形での記述になっている。たとえばデフォルトでは「User」であって「Users」ではない。しかしここでは「Admins」と複数形で指定している。
ここであえてCakePHPでのモデル名(テーブル名)のルールの解説はしないが、複数形への変更はCakePHPの内部処理でやってくれるのが常だと思っていたしドキュメントを見てもそうなっているのでずっとそうしていたのだが、lib/Cake/Controller/Components/Auth配下をずっと調べたりechoデバッグをしたりしていたら、そのことに気が付いたのだ。もしかしたらこれ、Config/databases.phpファイルでデータベースの設定でprefixを設定している影響があるのかもしれない。prefixオプションで’mytable’などと指定していると、モデル名をテーブル名に展開するときにCakePHP内部でprefixを付加した形でアクセスしてくれるというものだ。実装を深く追っていないが、もしかしたらprefixがあるときにuserModelへの指定は複数形の形にしておかないといけないのかもしれない。でもAuth以外での指定は複数形じゃなくても問題ないんだよね。まったく意味が分かりません……。

14行目のscopeオプションは、find()時のconditionsに付け足される検索条件になる。アカウントフィールドにenabledやdeletedなどのフラグがあり、これを参照させたい場合はここに条件付けを加えればよい。その下のfieldsオプションは、単なるフィールド対応表で、キー部にはinputタグのname属性に指定された文字列を入れ、それに対応するアカウントテーブルのフィールド名を値部に書けばいい。find()メソッドに指定するfieldsオプションとは違うので混乱しないように注意したい。同一名なら省略できるが、ここではあえて設定している。

/* と */でコメントアウトしている点は、例のPasswordHasher向けの設定で、Auth内で使用されるハッシュアルゴリズムなどを指定する。classNameオプションでここではSimpleを指定しているが、他のクラスを指定することで別のハッシュアルゴリズムを利用することもできる。独自クラスを定義して指定することもできる。なおデフォルトはSimpleクラスで指定されている。この件は後でさらりと。
その次のhashTypeオプションでは、SimplePasswordHasherクラスで実装されている複数のハッシュアルゴリズムのうちで何を使用するかを指定する。SimplePasswordHasherでは「md5」「sha1」「sha256」のいずれかを指定することができる。デフォルトではsha1が指定されている。これも後で簡単にふれてみたい。

で、コメントアウトしているということは、要するにデフォルトで使うということなので、Authコンポーネントで使用するハッシュアルゴリズムはSimplePasswordHasherクラスで実装されているsha1を使用するということなので、特に指定しなくてもよいということになる。備忘録的に書いた形にしている。なので、デフォルトで使用する人はこのコメント部分はばっさり記述を落とすことができる。
ただし今後デフォルトが変更される可能性がある。SHA-1アルゴリズムは2005年に攻撃法が見つかっていて2010年には米国政府で使用するアルゴリズムをSHA-1からSHA-2に変更するよう推奨する論文がNISTから出されている。ということで、今後しばらくSHA-1がデフォルトであり続ける保証もない。突然デフォルトアルゴリズムが変更された場合、CakePHPのバージョンを更新した瞬間に既存のアカウントはすべてログイン不能に陥ることになりかねない。ということで、デフォルトであっても実装しておくというのは一つの回避方法ではあると思う。

続いてLoginsController.php……あ、これは私がドキュメントを見てとりあえず実装したものなので、CakePHPドキュメントに書かれているものとたいして変わらない。適当に自分のものに適応させていただきたい。ログインを実装するコントローラーで、以下のlogin()メソッドを実装するようにする。これはまあ、CakePHPのドキュメントなどでもおなじみの実装パターンではなかろうか。この例ではSession::setFlash()メソッドを使用しているので、$componentsプロパティでSessionを指定しておく必要がある。

[php]
public function login()
{
if ($this->request->is(‘post’)) {
if ($result = $this->Auth->login($this->data)) {
return $this->redirect($this->Auth->redirectUrl());
}
$this->Session->setFlash(‘ユーザー名もしくはパスワードが違います.’, ‘default’, array(), ‘auth’);
}
}
[/php]

以下のViewファイルも同様にドキュメントに示された内容をもとに書いているだけなので、ドキュメントを読むほうがよいかもしれない。というか、まあ筋書き通りに使っていて特異なことは何もしていない。

[php]
if ($this->Session->check(‘Message.auth’))
echo $this->Session->flash(‘auth’) . ‘<br>’;
echo $this->Form->create(‘admins’, array(‘action’ => ‘login’));
echo $this->Form->input(‘username’, array(‘label’ => ‘ユーザー名’, ‘type’ => ‘text’));
echo ‘<br>’;
echo $this->Form->input(‘password’, array(‘label’ => ‘パスワード’, ‘type’ => ‘password’));
echo ‘<br>’;
echo $this->Form->end(‘ログイン’);
[/php]

実装部分はこれだけである。特徴的なのはAuthの設定部分だけとなるだろう。

[Sponsored Link]


さて、問題のパスワード発射ーじゃなくてパスワードハッシャーの話。PasswordHasherなるクラスにハッシュアルゴリズムが隠蔽されているのだが、それ自体はAuthのオプション設定で指定できるのは先ほど示したとおり。
しかしこれ単純はハッシュ値ではなくて、app/Config/core.phpのSecurity.saltで指定された値が加えられた上でハッシュが計算される。つまり単純にパスワードそのもののハッシュ値をアカウントテーブルのパスワードフィールドに突っ込んでもダメだということである。
ドキュメントなどではadd()やedit()などを使ってアカウントそのものの編集ができる画面構成をとっているが、私の場合はアカウントリストの表示は編集やパスワード変更をさせる予定がないため、テーブルに手動で直接アカウント情報を突っ込んでいる。
ということで、ハッシュ値の計算はコントローラーに暫定的に以下のようなmyhash()などのアクションを作っておき、このメソッドにパスワードを渡してアクセスし、ハッシュ値を取得する。ここで表示された値をテーブルに手動で突っ込むのだ。いはやは、なんともダサい。

[php]
<?php
public function myhash($pw = ”)
{
$this->autoLayout = false;

if (Configure::read(‘debug’) > 0) {
$ph = new SimplePasswordHasher();
echo $ph->hash($pw);
}
}
?>
[/php]

もちろん実装的には、このアクションを改造し、このアクションへのアクセスと同時にテーブルに突っ込むようなinsertmyhash()みたいなメソッドを実装すれば手動でやらなくても事足りるかもしれないが、セキュリティ的に危険に満ちた実装になること確定なので絶対にオススメできないのである。

というわけで今回のハマリどころは2点。

・Authに渡したパスワードはPasswordHasherクラスで処理し、さらにハッシュ値はパスワードにSecurity.salt値を加えたもので生成しアカウントテーブルと比較する。
・AuthのuserModelオプションで指定するモデル名は複数形で末尾に「s」を付ける必要があった。ただしもしかしたらdatabases.phpのデータベース設定でprefixオプションを使用しているからかもしれない(未確認)。

ということでありましたとさ……これで2週間潰した orz

Webアプリ開発を加速する CakePHP2定番レシピ119詳解CakePHP辞典―2.0/2.1/2.2/2.3対応WebデザイナーのためのCakePHPビューコーディング入門PHP+MySQLマスターブックよくわかるPHPの教科書 【PHP5.5対応版】

コメントを残す

メールアドレスが公開されることはありません。

このサイトはスパムを低減するために Akismet を使っています。コメントデータの処理方法の詳細はこちらをご覧ください