パスワードのハッシュ化
利用者の安全を守るため、パスワードは読める形では決して保存せず、その痕跡(ハッシュと呼びます)だけを保存します。ハッシュからもとのパスワードを逆算することはできません。ハッシュを作るには安全なアルゴリズムを使うことが大事です。Nette\Security\Passwordsクラスがそれを助けてくれます。
フレームワークは Nette\Security\Passwords 型のサービスを、security.passwords
の名前で DI コンテナに自動的に足します。dependency
injectionで渡してもらえます。
use Nette\Security\Passwords;
class Foo
{
public function __construct(
private Passwords $passwords,
) {
}
}
__construct(string $algo=PASSWORD_DEFAULT, array $options=[])
ハッシュを作るのにどの安全なアルゴリズムを使うかを選び、そのパラメータを設定します。
既定は PASSWORD_DEFAULT で、アルゴリズムの選択は PHP
に任されます。より新しく強いハッシュのアルゴリズムが現れれば、新しい PHP
のバージョンでアルゴリズムが変わることがあります。ですからできあがるハッシュの長さが変わりうることを踏まえて、十分な文字数を収められる形で保存すべきです。幅は
255 をおすすめします。
bcrypt のアルゴリズムのハッシュ化の速さを cost のパラメータで決める例です。(2020 年の既定は 10 で、パスワードのハッシュ化はおよそ 80 ミリ秒かかります。cost が 11 ならおよそ 160 ミリ秒、12 ならおよそ 320 ミリ秒です。遅いほど守りは強くなり、速さ 10〜12 ならすでに十分な守りと見なされます。)
// bcrypt のアルゴリズムを 2^12(2^cost)回繰り返してパスワードをハッシュ化します
$passwords = new Passwords(PASSWORD_BCRYPT, ['cost' => 12]);
dependency injection を使う場合です。
services:
security.passwords: Nette\Security\Passwords(::PASSWORD_BCRYPT, [cost: 12])
static bcrypt(?int $cost=null): Passwords
bcrypt のアルゴリズム向けに整えられたインスタンスを作ります。$cost
のパラメータは上で説明したハッシュ化の速さを決めます。省くと PHP の既定が使われます。
$passwords = Passwords::bcrypt(12);
static argon2id(?int $memoryCost=null, ?int $timeCost=null, ?int $threads=null): Passwords
Argon2id のアルゴリズム向けに整えられたインスタンスを作ります。省いたパラメータは PHP
の既定に任されます。PHP が Argon2 への対応なしにビルドされている場合、このメソッドは
Nette\NotSupportedException を投げます。
$passwords = Passwords::argon2id(memoryCost: 1 << 17, timeCost: 4);
hash(string $password): string
パスワードのハッシュを作ります。
$res = $passwords->hash($password); // パスワードをハッシュ化します
結果の $res
は文字列で、ハッシュそのもののほかに、使われたアルゴリズムの識別子、その設定、そして暗号のためのソルト(同じパスワードでも違うハッシュができるようにする無作為なデータ)を含みます。ですから後方互換があり、たとえばパラメータを変えても、前の設定で保存されたハッシュはそのまま確かめられます。この結果全体をデータベースに保存するので、ソルトや設定を別に保存する必要はありません。
verify(string $password, string $hash): bool
渡されたパスワードが渡されたハッシュと合うかを調べます。$hash
は、入力されたユーザー名やメールアドレスをもとにデータベースから取り出します。
if ($passwords->verify($password, $hash)) {
// パスワードが正しい
}
needsRehash(string $hash): bool
ハッシュがコンストラクタで指定したオプションに合っているかを調べます。
たとえばハッシュ化の cost
を変えたときに役立ちます。確認は保存された設定に従って行われ、needsRehash() が
true
を返すなら、今度は新しいパラメータでハッシュを作り直し、データベースに保存し直す必要があります。こうして利用者がログインするたびに、保存されたハッシュが自動的に「上げられ」ます。
if ($passwords->needsRehash($hash)) {
$hash = $passwords->hash($password);
// $hash をデータベースに保存します
}