読者です 読者をやめる 読者になる 読者になる

ハウテレビジョン開発者ブログ

『外資就活ドットコム』を日夜開発している技術陣がプログラミングネタ・業務改善ネタ・よしなしごとについて記していきます。

CakePHP2 で、phpass でハッシュ化されたパスワードが保存されたユーザーテーブルを扱えるようにする

こんにちは、エンジニアの@soyanaです。

CakePHP では、バージョン 2.4 から passwordhasher という仕組みが導入され、認証オブジェクトがパスワードハッシュの生成とチェックを行うために、新たなパスワードハッシュ化オブジェクトを使うようになりました。 これにより、デフォルトで用意されているパスワードハッシュ化アルゴリズム以外のものを使いたい場合に、容易に差し替えることができるようになっています。

既に WordPress など他のシステムにより生成されたユーザーテーブルをそのまま扱って Cakephp で開発し直すことがあるかもしれません。

そういった場合に、この passwordHasher という仕組みを使うと認証を少ないコードで容易にカスタマイズすることができます。

ここでは、WordPress からの移行を例にとって、その方法をご紹介していきます。

やることは次の3つです。

  • phpass を Vendor ディレクトリに置く
  • カスタムした PasswordHasher クラスの作成
  • $components に passwordHasher を指定

ユーザーモデル

まず、前提として、WordPress のユーザーデータが保存されているテーブル wp_users を扱うモデルが定義済みだとします。

<?php
// app/Model/User.php
class User extends AppModel {
    public $name = 'User';
    public $useTable = 'wp_users';
    public $primaryKey = 'ID';
 }

phpass を Vendor ディレクトリに設置

WordPress では、パスワードのハッシュ化に phpass というフレームワークを用いています。 WordPress のソースコードにも phpass クラスは同梱されてはいるのですが、 phpass 公式サイトで公開されているソースコードをダウンロードすることにします。 Portable PHP password hashing ("password encryption") framework からダウンロードした phpass-{version}.tar.gz を展開すると、PasswordHash.php が含まれています。フレームワークというほど大げさなものではなく、実体は250行の1つのクラスファイルです。これを app/Vendor/ ディレクトリ以下に配置します。app/Vender/phpass/passwordhasher.php として置かれたとします。

カスタムした PasswordHasher クラスの作成

CakePHP デフォルトのハッシュ化クラス Simple は sha1, sha256、md5 ハッシュを使えます. また、Blowfish password hasher も組み込みで用意されていますが、phpass は選べません。

phpass パスワードハッシュを使えるように、PasswordHasher クラスを Component として作成します。このクラスには、hash メソッドと check メソッドを実装します。 といっても、phpass に ハッシュ化する HashPassword メソッドと CheckPassword メソッドが用意されており、 対応するメソッドを呼ぶだけです

<?php
// app/Controller/Component/Auth/PhpassPasswordHasher.php
App::uses('AbstractPasswordHasher', 'Controller/Component/Auth');
App::import('Vendor', 'phpass/PasswordHash');

/**
 * Phpass password hashing class.
 */
class PhpassPasswordHasher extends AbstractPasswordHasher {

/**
 * Generates password hash.
 *
 * @uses PasswordHash::HashPassword
 * @param string $password Plain text password to hash.
 * @return string Password hash
 * @link http://www.openwall.com/phpass/
 */
    public function hash($password) {
        $hasher = new PasswordHash(8, true);
        return $hasher->HashPassword($password);
    }

/**
 * Check hash. Generate hash for user provided password and check against existing hash.
 *
 * @uses PasswordHash::CheckPassword
 * @param string $password Plain text password to hash.
 * @param string $hashedPassword Existing hashed password.
 * @return boolean True if hashes match else false.
 */
    public function check($password, $hashedPassword) {
        $hasher = new PasswordHash(8, true);
        return $hasher->CheckPassword($password, $hashedPassword);
    }

}

$components に passwordHasher を指定

認証の際のパスワードハッシュ化に phpass を使うよう、AppController.php にて上記で作成した passwordHasher を指定します

<?php
// app/Controller/AppController.php
class AppController extends Controller {
    public $components = array(
        'Auth' => array(
            'authenticate' => array(
                'Form' => array(
                    'userModel' => 'WpUser',
                    'fields' => array('username' => 'user_login', 'password' => 'user_pass' ),
                    'passwordHasher' => 'Phpass'
                )
            ),
    );

パスワード保存時のハッシュ化

新規ユーザ作成時やパスワードの変更する場合など、ユーザのパスワードをユーザーテーブルに保存する際に、phpass によりパスワードをハッシュ化するよう、User モデルの beforeSave メソッドに処理を書きます。

<?php
// app/Model/User.php
App::uses('PhpassPasswordHasher', 'Controller/Component/Auth');
 
class User extends AppModel {
    public function beforeSave($options = array()) {
        if (!$this->id) {
            $passwordHasher = new PhpassPasswordHasher();
            $this->data['User']['user_pass'] = $passwordHasher->hash($this->data['User']['user_pass']);
        }
        return true;
    }
}

これでできました。簡単ですね。

WordPress で構築されたシステムに登録されたユーザデータをそのまま維持して、CakePHP で作り変えることになっても、全ユーザのパスワードがリセットしなければなんてことにはならず、スムーズに移行することができますね。 また、他のハッシュアルゴリズムによりパスワードが保存されているユーザテーブルを扱う場合にも、同様の方法を適用することができるでしょう。

参考: 認証 — CakePHP Cookbook 2.x ドキュメント