Laravel 新しい Password ルールオブジェクトを試してみる。そして軽くショックを受ける。

2 min read読了の目安(約2000字

Laravel Ver.8.39 から、Password ルールオブジェクトというのが登場しました。
本家サイトのドキュメントに説明が記述されています。

https://laravel.com/docs/8.x/validation#validating-passwords

これは、以前からあるpasswordの組み込みルールとは別物です。
(そちらは、ログイン済みユーザーのパスワードと一致するかをチェックします)

新しい Password ルールオブジェクトを簡単に訳すと次のようになります。

// 最低8文字
Password::min(8)

// 1文字以上のアルファベットを含むこと
Password::min(8)->letters()

// 大文字と小文字のアルファベットを含むこと
Password::min(8)->mixedCase()

// 1文字以上の数字を含むこと
Password::min(8)->numbers()

// 1文字以上の記号を含むこと
Password::min(8)->symbols()

// 漏洩済みパスワードでないか
Password::min(8)->uncompromised()

// 組み合わせも可能
Password::min(8)
    ->letters()
    ->mixedCase()
    ->numbers()
    ->symbols()
    ->uncompromised()

最初は必ず、
Password::min(xx) から始めます。
また、記号を2文字以上とか、そういう指定はできません。

一番最後の気になる uncompromised() ですが、以下のサイトのAPIを呼び出して確認しています。
have i been pwned?

パスワードをそのまま送るのでは無く、ハッシュ化して先頭の5文字のみ送るので、安心して使えます。(詳しくは下で)

どのように漏洩の照合をするのか

例えば、パスワード abcd で検証するとします。まずは、

$hash = strtoupper(sha1((string) $value));

で、ハッシュ値を得ます。そしてそれの先頭5文字とそれ以降に分けます。

(a) 81FE8
(b) BFE87576C3ECB22426F8E57847382917ACF

(a)の先頭5文字を使って、上記サイトにチェックを投げます。
すると、ハッシュ化した先頭5文字が一致するパスワードのハッシュ値を一覧で返してきます。その際、その先頭5文字はカットして、後ろに何回漏洩したかの情報をコロン区切りで付与して返してきます。
以下のような感じです。

BF890CE7ED8688F49F89B2C88CADABCE63F:1
BFB5505A80C50A1217261BD826AB4DCC197:1
BFE87576C3ECB22426F8E57847382917ACF:69484
C024DCD92C69BE6EF1C6E40E37E88F4A404:2
C086C0F3EC9A74A28C9532538FD85C06326:4
(~実際はもっと沢山返ってきます~)

この場合、69484回漏洩したという「BFE87576C3ECB22426F8E57847382917ACF」が、
先の(b)と一致する為、パスワード abcd は漏洩済みと判定されます。

チェックの際、この回数に関してはカスタマイズでき、3回まではOKとするなら、

Password::min(8)->uncompromised(3);

のように書けます。
(補足:ドキュメントは、less than と書いてあるけど、equal to or less than が正しい模様)

早速自分のパスワードが漏れてないかチェックしてみる

私の場合、普段使うパスワードは10個用意しています。それらに対して、uncompromised() でチェックしてみました。すると、ショッキングな事に、内4つが漏洩済みと判定されました…。

漏れていると判定された4つの内3つは、7~9文字のアルファベットのみのパスワード。これではいけなかったと反省です。

実は、パスワードとメールアドレスの同じ組を使い回さないよう、メールアドレスは40個位用意していますが、とは言えパスワードが漏れているなら、これは変えないといけませんね…。

雑感

この漏洩チェック機能、クライアントに提案してみるのもいいかも知れません。
(少し、見積金額アップできるかも)