CakePHP3 で平のパスワードをハッシュ化する方法

様々な会員制サイトやログインが必要な管理画面等を構築する場合、ID と Password を組み合わせて認証させるケースがあります。この場合、ユーザが送信した ID と Password が DB に保存されている値と一致するかチェックする仕組みを実装するのが一般的です。ただし、Password が平文のまま DB に保存されていると、万が一情報が漏れてしまった場合に別のハッキングを誘発することになり責任が重大です。

本記事では、CakePHP3 の標準機能にある DefaultPasswordHasher を用いてハッシュ化されたパスワードを DB に保存しつつ、ユーザが送信する平のパスワードとハッシュ化されたパスワードが一致するか検証する方法を記述します。なお、認証ロジックを実装する方法はこちらを参照ください。

DefaultPasswordHasher の利用

実装方法は単純で、以下のマニュアルのようにパスワードをハッシュ化したいエンティティにアクションを記述します。これを実装すると、データベースに値が保存される直前でハッシュ化されるため、パスワードのバリデーションに影響されない設計となっています。

CakePHP3 公式マニュアル パスワードのハッシュ化

namespace App\Model\Entity;

use Cake\Auth\DefaultPasswordHasher;
use Cake\ORM\Entity;

class Member extends Entity
{
    // ミューテーターの _set 以降、password がカラム名
    protected function _setPassword($password)
    {
          return (new DefaultPasswordHasher)->hash($password);
    }
}

AuthComponent は標準で DefaultPasswordHasher を利用するため、特別な理由がない限りパスワードのハッシュ化はこれを利用した方が無難と思われます。なお、この場合のハッシュ化アルゴリズムは bcrypt になります。この方法を用いてパスワードのハッシュ化を行う場合、CakePHP3 では php の password_hash 関数を利用するため、ハッシュ化された値の長さは 60 文字です。ただし、今後変更される可能性もあるので、php の公式マニュアルに従ってDB 設計時のカラムの長さは 255 に指定した方が無難と思われます。

php 公式マニュアル パスワードのハッシュ化

上記処理内、_set から始まるメソッドはミューテーターと呼ばれるもので、プロパティがどのように保存されるべきか指定するものです。_set + プロパティ名(カラム名)で定義する必要があり、キャメルケースで記述しなければいけません。今回は password カラムを利用するので、_setPassword としました。なお、指定するフォーマットを返却する必要があるので、return の後ろに続けてフォーマットを記述しました。

CakePHP3 公式マニュアル アクセサーとミューテーター

ハッシュ化の確認

早速ハッシュ化した値をデータベースに保存して確認してみます。エラーチェックやアソシエーションなど色々やりたいことがありますが、今はハッシュ化についてフォーカスするために一旦無視します。コントローラ内、パスワードの保存を行いたい箇所で以下のように記述します。

$member = $this->Members->newEntity();
if ($this->request->is(['post', 'put'])) {
    $member = $this->Members->patchEntity($member, $this->request->data);
    $this->Members->save($member);
}

これでコントローラ内でパスワードを保存する準備ができました。Templete ファイルにパスワードを POST できるように記述して、パスワードを送信してみてください。データベースの中に無事ハッシュ化された値が格納されているかと思います。

平のパスワードとハッシュ化されたパスワードの一致

上記でパスワードをハッシュ化して DB に保存することができました。このままではユーザが送信する平のパスワードとハッシュ化されたパスワードが一致しませんので、一致しているか確認する方法を記述します。

例えばログインアクションなど、ユーザが ID とパスワードを POST するアクションへ以下のようにコードを実装すれば、一致しているかどうか自動で判別し、一致していればユーザ情報を返却してくれます。

$member = $this->Members->newEntity();
if ($this->request->is('post')) {
    // ログインのチェック
    $user = $this->Auth->identify();
    if ($user) {
        // 最新のユーザ情報をセット
        $this->Auth->setUser($user);

        // ログイン成功時のリダイレクト
        $this->Flash->success('ログインに成功しました');
        return $this->redirect($this->Auth->redirectUrl());
    } else {
        $this->Flash->error('ログインに失敗しました');

        // ログイン失敗時のリダイレクト
        return $this->redirect(['action' => 'login']);
    }
}

上記のように実装することで、自力でハッシュ化されたパスワードの取り扱いを記述することなく一致しているかどうかのチェックするロジックが実装完了しました。

よりコアな情報

平のパスワードとハッシュ化されたパスワードの比較ですが、CakePHP に全てを任せるのではなく、あくまでも平文とハッシュ化された文字列の比較のみ行いたい場合は、以下のように CakePHP3 が標準で準備しているものを利用します。マニュアル内で解説されている箇所を見つけられなかったので、ソースコード内、実装されている箇所を以下に挙げます。

//ファイルの置き場所
app/vendor/cakephp/cakephp/src/Auth/DefaultPasswordHasher.php

class DefaultPasswordHasher extends AbstractPasswordHasher
{
    public function check($password, $hashedPassword)
    {
        return password_verify($password, $hashedPassword);
    }
}

コントローラ内に以下を記述して、直接値を入力してみてどのような結果が得られるか確認してください。

//現在のパスワードの確認
$hasher = new DefaultPasswordHasher();
if($hasher->check('平のパスワード', 'ハッシュ化されたパスワード')){
    debug('Password is Correct');
}else{
    debug('Password is Wrong');
}

無事平文とハッシュ化された文字列の比較が成功すると思います。

終わりに

何か不明点があれば、サイト TOP のお問い合わせよりご質問ください。また、より CakePHP3 について詳しく知りたい方は、現在公式マニュアルの翻訳を行っていますので一緒にやりませんか?

CakePHP3 の公式マニュアルを翻訳する方法

Enjoy!!