パスワード保存のお供に Crypt::SaltedHashB!

こんにちは。はてなでは id:sfujiwara、それ以外のところでは fujiwara です。

Webサービスなどでユーザーのパスワードを預かる場合、「一方向ハッシュ関数を通した値を保存せよ」というのはよく知られた話だと思いますが、単に MD5 や SHA1 の値を保存するだけでは安全性に問題があります。

例えば Digest::MD5::Reverse というモジュールを使うと、MD5 の値を逆算することができてしまいます。

use Digest::MD5::Reverse;
print reverse_md5("388c3c9c00e651cc163cbdd47f08c427"); # fujiwara

実際には計算で求めているわけではなく、http://md5.rednoize.com/ のようなハッシュ値を収集しているサイトから結果を得ているので、任意の値について逆算できるわけではないのですが、このように比較的容易に逆算できてしまっては危険ですね。

# 他にも Googleで検索するとか……

ということで、このような場合は salt というランダムな値と一緒にハッシュ関数を通すことで、容易に逆引き一覧を用意できないようにして対抗するのが定石です。それを簡単にやってくれるのが Crypt::SaltedHash です。

文字列をハッシュ化するには

use Crypt::SaltedHash;
my $csh = Crypt::SaltedHash->new(algorithm => 'SHA-1');
$csh->add('secret');
print $csh->generate; # {SSHA}aeABWjt7Wq/UkrqTtUh9PbyoAnNndtSn

これで OK です。algorithm は省略すると SHA-1 になりますが、他にも MD5, SHA-256, SHA-512 などが指定可能です。salt が効くので、同じ入力値でも実行するたびに違う結果が得られます。

ハッシュ化された値から元の値を検証するには

$valid = Crypt::SaltedHash->validate($salted, 'secret');

これで真偽値が返ります。簡単ですね!

このモジュールがやってることは要するに、「乱数で salt を用意してもとの値と結合してからハッシュ関数に通す」だけなので、それぐらいなら自分でコード書いてもいいじゃないかと思われる方もいるかも知れません。

が、Crypt::SaltedHash が生成する値は、LDAP のパスワードを扱う方法を定義した RFC-3112 に準拠した形式になるというメリットがあります (ただし SHA-1, MD5 を使用した場合のみ)。

ある日突然、ユーザアカウントの管理を LDAP に移行したい! ということになっても、パスワードカラムの値をそのまま使えるのです。素晴らしいですね。

# え、そんなこと滅多にない?

明日は Songmu さんです。お楽しみに!