CakePHP3を触ってみました 〜SHA1(シャア)という名のオールドタイプ〜

この記事はだいぶ前に書かれたものなので情報が古いかもしれません
偉い人にはそれが分からんのですよのシーン

この記事を三行にまとめると

パスワードの入力しても認証が通りません
通常の三倍の早さで実装できることを保証しよう
FallbackPasswordHasherを使います
以前、CakePHP3でパスワードをハッシュ化する方法を書いたことがあります。

パスワードのハッシュ化はどうやるんだ?

この時は「DefaultPasswordHasher」というクラスを使ってハッシュ化を行いました。

新規にウェブサービスを作る場合などはこれで問題ないのですが、もしもすでにCakePHP2で作ったサイトが動いている場合、それをCakePHP3に移行する場合、ハッシュ化の方式が変わっているので、そのままではログイン画面などでパスワードの入力しても認証が通りません。

CakePHP2ではSHA1という方式でハッシュ化しています。セキュリティレベルとしてはちょい弱いみたい。オールドタイプの方式なんですね。シャアなんて名前ついてるのにね。SHAだから正確には「シャ」だけど。DefaultPasswordHasherではとbcryptという方式でハッシュ化するようですが、詳しいアルゴリズムについてはよう分からんです。

ってなことで、CakePHP2で登録したパスワードをそのまま使うなら、シャア大佐に活躍してもらう必要があります。でも大丈夫。難しいことは何もない。この記事を読めば通常の三倍の早さで実装できることを保証しよう。

嘘です。通常の早さ(よりもちょい遅いくらい)でお願いします。



パスワードをチェックする場合

例えば、UsersControllerのログイン画面でフォームにパスワードを入力すると仮定した場合。

namespace App\Controller
use Cake\Auth\FallbackPasswordHasher;
class UsersController extends AppController {
  public function initialize() {
    parent::initialize();

    $this->loadComponent('Auth', [
      'authenticate' => [
        'Form' => [
          'passwordHasher' => [
            'className' => 'Fallback',
            'hashers' => ['Weak']
          ]
        ]
      ]
    ]);    
  }

  public function login() {
    //ログイン処理
  }
}

こんな感じっす。「DefaultPasswordHasher」の代わりに「FallbackPasswordHasher」を使うように設定しています。この「FallbackPasswordHasher」ってのが、昔のハッシュ化方式を呼び出せるクラスのようですね。「’hashers’ => [‘Weak’]」と書くことによって、SHA1でハッシュ化されたパスワードの整合性を確認してくれます。

これだけでOKです。やり方さえ分かればどうということはない。



パスワードを変更する場合

新規にパスワードを登録する場合や変更する場合。より安全のためにDefaultPasswordHasherでハッシュ化した方が良いとは思うんですが、もしSHA1でハッシュ化したいならやっぱりFallbackPasswordHasherを使います。

//DefaultPasswordHasherでのハッシュ化
$hasher = new DefaultPasswordHasher();
$password = $hasher->hash('test1234');

//FallbackPasswordHasherでのハッシュ化
$hasher = new FallbackPasswordHasher(['hashers' => ['Weak']]);
$password = $hasher->hash('test1234');

ここでも「Weak」の指定が必要になります。シャアにお前は弱いと徹底的に突きつけてやるわけですね。本物のシャアは弱くないから、あまり言いすぎるとあとで粛清されるかもしれん。



SHA1とbcryptの共存

チェックはSHA1で行なっているのに、登録や変更をDefaultPasswordHasherを使ってbcryptでハッシュ化してしまうと、結局認証が通らない状態になってしまいます。

それを回避するには、SHA1とbcrypt、両方でチェックできるようにする必要がある。オールドタイプとニュータイプの共存が必要になるわけですね。

//UsersControllerのinitialize
$this->loadComponent('Auth', [
  'authenticate' => [
    'Form' => [
      'passwordHasher' => [
        'className' => 'Fallback',
        'hashers' => ['Weak', 'Default']
      ]
    ]
  ]
]);

hashersに複数の方式を指定することで、順番にチェックを行なってくれます。上記の場合、WeakがSHA1でDefaultがbcryptに相当します。





こんなとこですかねー。

もし上記の方法を試したのにパスワードがエラーになるって場合は、Security.saltが違ってる可能性があるかもしれない。ちゃんとCakePHP2のcore.phpで書いたSecurity.saltの文字列をCakepHP3のapp.phpにコピペってくださいな。

CakePHP2から3への移行って、何かとスムーズに行かないっすよね。僕も今、そこそこでかい規模のシステムを全面的に改修するにあたり、2から3へ移行することになったんですけど、でも3に移行すると確実に2のままで行く場合の三倍の工数が予想される。納期を大幅に、おそらくは数ヶ月単位で遅らせなければならない。偉い人にはそれが分からんのですよ。この場合の偉い人(一番の責任者)ってーのは僕自身なんで、別に自分の負担が増えるだけで誰が困るってわけでもないんですがね。

まあ僕のことはともかく、従来のパスワードを使うにはどうすりゃいいんだって悩んでる人にこの記事の情報がお役に立てば幸いっす。

問題解決の栄光を君に。



その他のCakePHP3を触ってみましたの記事はこちら
まとめという名の箸休め
 もしかしたら何か関連しているかも? 
 質問や感想などお気軽にコメントしてください