暗号 2021年06月19日 00:51   編集
Web上で使用するパスワードはほとんど半角英数字と記号を使うので、全角の日本語は使えないと思っていたが、必ずしもそういうわけではないようだ。この用語集作成スクリプトterm.phpや掲示板スクリプトbbs.phpでも、管理者認証などのためパスワードを設定することになっているが、試しに日本語のパスワードを設定したところ、問題なく使えた。

しかし、一般的には日本語のパスワードは使えないということになっている。たぶんパスワードの入力に のようなpasswordフィールドを使っているからだと思う。このpasswordフィールドは、入力した文字が伏せ字になるだけでなくIMEが無効になっているので、日本語が入力できないのだ。直接入力するのではなくコピーアンドペーストすれば入力できないことはないが、半角文字が使われていないとかいわれて受け付けられない場合もあるだろう。
パスワードの入力にpasswordフィールドを使っているのは、入力画面を盗み見るショルダーハッキング対策だと思うが、入力した本人にも見えないので、正しく入力できているのか確認しにくいという問題もある。(特に新しいパスワードを登録する場合)
パスワードを登録する場合、決められた文字種(半角数字、半角英大文字、半角英小文字、記号)をすべて使わないと受け付けられない場合や、逆に指定された文字種しかパスワードに使えない場合があって、登録しようと思ったパスワードが登録できないことがたびたびある。
指定した文字種をすべて使えというのは、パスワードに使用する文字種が多いほど、総当たりによるハッキングで破られにくくなるからだろうが、すべて使ったとしても半角英数字で64文字、記号を入れても100文字にも満たない。文字種が多いほど安全なパスワードが作れるなら2000文字以上が使える日本語の方が遙かに強力なパスワードが作れると思うのだが。

試しに総当たり攻撃で解読にかかる時間を計算してみると、日本語だと5文字のパスワードで解読に117世紀かかるという計算になった。十分な強度だ。日本語は2200文字が使えて[1]、1秒間で14万個のパスワードを解析できるとしての計算だ。
22005÷(140000×3600×24×365×100) = 117
半角英数字記号96文字によるパスワードだと、5文字は1日で解読される。6文字で65日、7文字で17年、8文字でやっと16世紀だ。ちなみに日本語で8文字にすると、解読に1兆世紀かかる計算になる。やはり累乗の底の違いは大きい。

それと半角英数字+記号で無理矢理作ったパスワードは覚えにくいという問題がある。複数のサイトで同じパスワードを使い回すなら覚えられるかもしれないが、サイトごとに変えるとなるとまず自分では覚えられない。日本語が使えるならいくらでも覚えやすいパスワードが作れそうだ。

ということで、最近公開した掲示板スクリプトbbs.phpでは、実験的に投稿フォームの編集パスワード入力フィールドをpasswordではなく、通常のtextフィールドで入力するようにしてみた。パスワードをクッキーに保存しておくと、最初から入力された状態で表示するので、その場合はpasswordフィールドで表示する。

Web上でのパスワードの保存と認証

Web上でパスワードを使用する場合、ユーザーが登録したパスワードはハッシュ化された上で保存されていると思う。ハッシュ化されたパスワードから元のパスワードを知ることはできないので、これが万一漏洩してしまっても、安全が保たれるからだ。ハッシュ化されたパスワードから元のパスワードを知ることはできないが、認証の際はこのハッシュ化されたパスワードと照らし合わせることによって入力されたパスワードが正しいかどうか判別することができる。これで登録した本人しかパスワードを知らなくてもパスワード認証が成立する。

このサイトで公開しているスクリプトは管理者パスワードも含めてパスワードはすべてハッシュ化して保存する仕様になっている。
PHPではハッシュ化はcryptやpassword_hashを使うが[2]、password_hash関数では日本語の文字列も普通にハッシュ化できる。password_verifyによる認証も問題なく行える。

password_hash覚書

password_hashに限った話ではないが、日本語パスワードだと半角英数字に比べると有効と認められる文字数が少なくなる。
たとえば「国境の長いトンネルを抜けると雪国であった。夜の底が白くなった。」をパスワードにしようとすると、パスワードとして認識されるのは「国境の長いトンネルを抜けると雪国であった。夜の底」までで、25文字以降は無視される。「国境の長いトンネルを抜けると雪国であった。夜の底が抜けた!」としても同じパスワードと認識されるということだ。半角文字だと72文字まで認識される。日本語だと1文字が半角文字3個分とカウントされていることになる。24文字も使えればパスワードとしては十分な長さで、perlのcrypt関数の半角8文字までとかに比べると全然問題ないレベルではある。
パスワードの暗号化についての覚書参照

書式は
$hashed = password_hash('パスワードの文字列',PASSWORD_DEFAULT);
のような感じ。PASSWORD_DEFAULTはハッシュ化のアルゴリズムの指定。現状ではPASSWORD_BCRYPTというアルゴリズムを指定したのと同じだが、PASSWORD_DEFAULTにしておくと、将来PHPにさらに強力なアルゴリズムが追加された場合、それに変更される可能性がある。PASSWORD_BCRYPTだと、60文字のハッシュが生成されるが、アルゴリズムが変わった場合、生成されるハッシュの長さが変わるかもしれない。
それとオプションでPASSWORD_BCRYPTアルゴリズムのコストを変更できるらしい。コストとは具体的になんなのかよくわからないが、はっきりしているのはこのコストを上げるとハッシュ化にかかる時間が増え、照合にかかる時間も増えるということだ。デフォルトは10で、ウチの環境の場合ハッシュ化もpassword_verifyによる照合にも0.046秒ぐらいかかる。これを
$hashed = password_hash('パスワードの文字列',PASSWORD_DEFAULT,  ["cost" => 11]);
として11に上げると倍の0.092秒ぐらいかかるようになる。12にするとさらに倍の0.18秒だ。
コストを上げるほど強力なパスワードが作成できるらしい。どう強力になるかはわからないが、少なくともコストを1上げると解読にかかる時間が2倍に増えることになる。
[1]実際にはもっと多い
[2]phpではmd5などのハッシュ関数も使えるが、パスワードのハッシュ化には推奨されていない
counter:2,657