PHP Manual
/
セキュリティ

文字列とパスワードのハッシュ化

11. 09. 2019

Obsah článku

ハッシュ化処理では(暗号化とは対照的に)入力から元の文字列がもはや導き出せないような出力を生成する。

そのため、機密性の高い文字列やパスワード、チェックサムの保護に適しています。

ハッシュ関数のもう一つの良い点は、常に同じ長さの出力を生成し、入力が少し変わると常に出力全体が完全に変化することである。

ハッシュ関数

PHP には多くのハッシュ関数がありますが、重要なものは以下のとおりです。

  • Bcrypt: password_hash() - 最も安全なパスワードハッシュ、計算が遅い、内部のソルトを使用し、繰り返しハッシュする。
  • md5() - ファイルのハッシュ化に適した非常に高速な関数です。出力は常に32文字です。
  • sha1() - ファイルハッシュのための高速なハッシュ関数。常に40文字で出力されます。

ハッシュ化

$password = 'ひみつパスワード';
echo password_hash($password); // Bcrypt
echo md5($password);
echo sha1($password);

Warning: md5()sha1() もパスワードのハッシュ化には適していません。なぜなら、元のパスワードを発見すること、あるいは少なくともパスワードを事前に計算することは計算上容易だからです。パスワードのハッシュ化のために開発された bcrypt を使用する方がはるかに良い。

md5cracker.com というサイトにはチェックサム(ハッシュ)のデータベースがあり、hash: 79c2b46ce2594ecbcb5b73e928345492 を検索してみてください、ご覧のように、純粋な md5() は一般的な単語やパスワードに対してはそれほど安全ではないわけですね。

唯一の正しい解決策:「Bcrypt + salt」。

How not to mess up in target plane という講演で、David Grudl は、パスワードを適切にハッシュ化して保存する方法について述べました。

唯一の正しい解決策は、「Bcrypt + salt」です。

具体的には

$password = 'ハッシュ';
// 安全なハッシュを生成する
echo password_hash($password, PASSWORD_BCRYPT);
// あるいは、より高い複雑さを持つ(デフォルトは10)。
echo password_hash($password, PASSWORD_BCRYPT, ['コスト' => 12]);

Bcripの利点は、主にスピードと自動塩分補給にあります。

生成に100ミリ秒と長い時間がかかるため、攻撃者が多くのパスワードをテストするのに非常にコストがかかります。

また、出力されるハッシュは自動的にrandom saltで処理されるため、同じパスワードを繰り返しハッシュ化した場合、常に異なるハッシュが出力されることになります。したがって、攻撃者は事前に計算されたハッシュテーブルを使用することができない。

そのため、ハッシュ化を繰り返すことでパスワードの正しさを確認することはできず、専用の関数を呼び出す必要がある。

if (password_verify($password, $hash)) {
// パスワードは正しく設定されています。
} else {
// パスワードが正しくありません
}

パスワードソルティング

ハッシュクラッキングをより困難にするために、元の入力に何らかの追加文字列を挿入するのは良いアイデアである。理想はランダムなもの。この処理を password salting と呼びます。

このセキュリティは、攻撃者がソルトを知らないため、事前に計算されたパスワードとハッシュのテーブルを使うことができず、パスワードを個別に解読しなければならないという考えに基づいている。

例えば、こんな感じです。

$password = 'secret_passport';
$salt = 'fghjgtzjhg';
$hash = md5($password . $salt);
echo $password; // オリジナルのパスワードを表示する
echo $hash; // ソルトを含むパスワードのハッシュを表示する

複合ハッシュ関数

ハッシュ関数を繰り返し実行することで、元のパスワードを繰り返しハッシュ化する必要があるため、クラックの複雑さが増すとお考えの方もいらっしゃるかもしれません。

例えば、こんな感じです。

$password = '暗証番号';
for ($i = 0; $i <= 1000; $i++) {
$password = md5($password);
}
echo $password; // md5() により 1000 倍ハッシュ化されます。

逆説的だが、突破の難易度は下がるか、ほとんど変わらない。

なぜなら、md5()関数は非常に高速で、通常のコンピュータで1秒間に100万以上のハッシュを計算できるため、パスワードを1つずつ試してもそれほど速度が落ちないからです。

2つ目の理由は、より理論的なもので、いわゆる衝突に遭遇する可能性があるということです。パスワードを繰り返しハッシュ化すると、時間が経つにつれて、攻撃者がすでに知っているハッシュがヒットすることがあり、これによって攻撃者はデータベースを使用してパスワードをハッシュ化することができるようになります。

したがって、低速で安全なハッシュ関数を使用し、ハッシュを一度だけ実行し、最終出力をソルティングで処理する方が良いのである。

2つのハッシュ/文字列の極めて安全な比較

パスワード認証におけるハッシュ比較において、===演算子が最も安全な選択ではないことをご存知でしょうか?

文字列を比較する場合、2つの文字列を1文字ずつ調べていき、最後に到達するか(成功、両者は同じ)、違いがない(文字列は異なる)かを判断します。

そして、これは攻撃で悪用される可能性があります。時間を正確に測れば、あと何文字足せば完全一致で最後まで到達するのか、あるいは文字列を比較するときにどこまで進んだのかを推定することができるのである。

解決策としては、文字列が比較される場所では必ずhash_equals()関数を使用し、比較に失敗した位置を攻撃者が知ることができれば問題ないでしょう。

そして、この機能はどのように行うのでしょうか?どの2つの文字列の比較も常に同じ時間がかかるようにし、どこで差が出たのか時間を測ってもわからないようにしています。私は、いくつかのタイプの攻撃は、本当に非常に可能性が低く、実装が困難だと思います。これもその一つです。

Jan Barášek   Více o autorovi

Autor článku pracuje jako seniorní vývojář a software architekt v Praze. Navrhuje a spravuje velké webové aplikace, které znáte a používáte. Od roku 2009 nabral bohaté zkušenosti, které tímto webem předává dál.

Rád vám pomůžu:

Související články

1.
5.
Status:
All systems normal.
2024