ホームページ > php教程 > php手册 > phpのハッシュ関数を理解してパスワードのセキュリティを強化する

phpのハッシュ関数を理解してパスワードのセキュリティを強化する

WBOY
リリース: 2016-06-21 08:55:24
オリジナル
1463 人が閲覧しました
1. 免責事項
未確認動物学は複雑なテーマであり、私はその専門家ではありません。多くの大学や研究機関がこの分野で長期にわたる研究を行っています。この記事では、Web アプリケーションのパスワードをできるだけシンプルでわかりやすい方法で安全に保存する方法を紹介したいと思います。
2. 「ハッシュ」とは何をしますか?
「ハッシュは、データの一部 (小さいデータまたは大きいデータ) を文字列や整数などの比較的短いデータに変換します。
これは一方向に依存することで実現されます。」ハッシュ関数。いわゆる一方通行とは、元に戻すことが難しい(または実際には不可能)ことを意味します。一般的なハッシュ関数の例は md5() で、これはさまざまなコンピューター言語やシステムで一般的です。

コードをコピー コードは次のとおりです。


$data = "Hello World"
$hash = md5 ($data ; そういえば、128ビット(16バイト)の整数でも表現できます。 md5() を使用して非常に長い文字列やデータを処理できますが、常に取得されるのは固定長のハッシュ値であり、この関数が「一方向」である理由を理解するのにも役立ちます。

3. ハッシュ関数を使用してパスワードを保存します


一般的なユーザー登録プロセス:
ユーザーは、パスワード フィールドを含む 登録フォーム
フォームに入力します。 >プログラムはユーザーが入力したすべての情報をデータベースに保存します。
ただし、パスワードはデータベースに保存される前にハッシュ関数によって暗号化されます。
元のパスワードはどこにも保存されないか、破棄されます。 。 ユーザーのログインプロセス: ユーザーはユーザー名とパスワードを入力します。
プログラムは、登録されているものと同じハッシュ関数を使用してパスワードを暗号化します。
プログラムはデータベースからユーザーを検索し、ハッシュ化されたパスワードを読み取ります。 ;
プログラムはユーザー名とパスワードを比較し、一致する場合にユーザーを承認します。
パスワードを暗号化する適切な方法を選択する方法については、この記事の後半で説明します。

4. 質問 1: ハッシュ衝突

ハッシュ衝突とは、2 つの異なるコンテンツが同じハッシュ値を取得するためにハッシュされることを意味します。ハッシュ衝突の可能性は、使用されるハッシュ アルゴリズムによって異なります。
生成方法は?
たとえば、一部の旧式のプログラムは crc32() を使用してパスワードをハッシュします。このアルゴリズムはハッシュ結果として 32 ビット整数を生成します。これは、可能な出力結果が 2^32 (つまり 4,294,967,296) しかないことを意味します。 パスワードをハッシュ化しましょう:


コードをコピーします

コードは次のとおりです:

echo crc32('supersecretpassword' ); // 出力: 323322056

次に、ある人物がデータベースを盗み、ハッシュ化されたパスワードを取得したと仮定します。 323322056 を「supersecretpassword」に復元することはできないかもしれませんが、同じ値にハッシュできる別のパスワードを見つけることはできます。これには非常に単純なプログラムのみが必要です。


コードをコピー

コードは次のとおりです。

set_time_limit(0); >$ i = 0; while (true) { if (crc32(base64_encode($i)) == 323322056) {

echo base64_encode($i); }

$i++;
}


このプログラムは実行に時間がかかる場合がありますが、最終的には文字列を返します。この文字列を「supersecretpassword」の代わりに使用し、そのパスワードを使用してユーザー アカウントに正常にログインすることができます。
たとえば、上記のプログラムをコンピューターで数か月間実行した後、「MTIxMjY5MTAwNg==」という文字列が得られました。テストしてみましょう:



コードをコピー

コードは次のとおりです。


echo crc32('supersecretpassword'); // 出力: 323322056

echo crc32('MTIxMjY5MTAwNg==') // 出力: 323322056
どうやって解決しますか?
現在、もう少し強力な家庭用 PC はハッシュ関数を 1 秒あたり 10 億回実行できるため、より広範囲の結果を生成できるハッシュ関数が必要です。たとえば、md5() は 128 ビットのハッシュ値を生成できるため、340,282,366,920,938,463,463,374,607,431,768,211,456 個の出力が可能になります。したがって、一般的に、ハッシュの衝突を見つけるためにそれほど多くのループを実行することはできません。ただし、まだこれを行う方法を見つけている人もいます。詳細については例を参照してください。
sha1() は、最大 160 ビット長のハッシュ値を生成するため、より良い代替手段です。
5. 問題 2: レインボーテーブル
衝突の問題を解決したとしても、まだ十分に安全ではありません。
「レインボー テーブルは、一般的に使用される単語とその組み合わせのハッシュ値を計算して構築されたテーブルです。」
このテーブルには数百万、さらには数十億のデータが格納される可能性があります。ストレージが非常に安価になったので、非常に大きなレインボー テーブルを構築できるようになりました。
ここで、ある人物がデータベースを盗み、何百万ものハッシュ化されたパスワードを入手したと仮定してみましょう。泥棒はレインボー テーブルでこれらのハッシュを 1 つずつ簡単に検索し、元のパスワードを取得できます。すべてのハッシュ値がレインボー テーブルで見つかるわけではありませんが、いくつかは確実に見つかります。
どうやって解決しますか?
次の例のように、パスワードに何らかの干渉を追加してみることができます。

コードをコピーします コードは次のとおりです。


$password = "easypassword";
// これはレインボー テーブルで見つかる可能性があります
// パスワードには 2 つの一般的な単語が含まれているため
echo sha1($password) ; // 6c94d3b42518febd4ad747801d50a8972022f956
/ / ランダムな文字を使用し、これよりも長くなる可能性があります
$salt = "f#@V)Hu^%Hgfds"
// これは見つかりません。事前に構築されたレインボー テーブル
echo sha1($salt . $password); // cd56a16759623378628c0d9336af69b74d9d71a5


ここで行うことは、各パスワードの前に干渉文字列を追加してハッシュするだけです。追加された文字列が十分に複雑であるため、ハッシュ値は事前に構築されたレインボー テーブルでは絶対に見つかりません。しかし、今でも十分に安全とは言えません。
6. 質問 3: 依然としてレインボー テーブル
マージ文字列を盗んだ後にレインボー テーブルが最初から作成される可能性があることに注意してください。干渉文字列もデータベースと一緒に盗まれる可能性があり、その後、この干渉文字列を使用してレインボー テーブルを最初から作成することができます。たとえば、「easypassword」のハッシュ値は通常のレインボー テーブルに存在する可能性がありますが、新しいレインボー テーブルには存在します。レインボーテーブルには「f#@V)Hu^%Hgfdseasypassword」のハッシュ値も存在します。
それを解決するにはどうすればよいですか?
ユーザーごとに固有の気を散らす文字列を使用できます。利用可能な解決策の 1 つは、データベース内のユーザー ID を使用することです:

コードをコピー コードは次のとおりです:


$hash = sha1( $user_id . $password);


このメソッドの前提は、ユーザーの ID が定数値であることです (これは一般的なアプリケーションの場合です)
それぞれをランダム化することもできますuser 固有の干渉文字列を生成しますが、この文字列も保存する必要があります:

コードをコピー コードは次のとおりです:


// 22 文字の長さのランダムな文字列を生成します
function unique_salt() {
return substr(sha1(mt_rand()),0,22)
$unique_salt = unique_salt();
$hash = sha1($unique_salt . $password);
// $unique_salt をユーザー レコードとともに保存します
// ...


この方法では、各パスワードが異なる文字列に干渉されるため、レインボー テーブルによる侵害を防ぎます。攻撃者はパスワードと同じ数のレインボー テーブルを作成する必要がありますが、これは現実的ではありません。
7. 質問 4: ハッシュ速度
ほとんどのハッシュ アルゴリズムは、通常、大規模なデータまたはファイルの整合性を検証するためにハッシュ値を計算するために使用されるため、速度を念頭に置いて設計されています。データの正確さと完全性。
生成方法は?
前に述べたように、強力な PC は 1 秒あたり数十億回の計算を実行できるため、総当たり攻撃を使用してすべてのパスワードを試すことが簡単になります。 8 文字以上のパスワードを使用すればブルート フォース クラッキングから保護できると思うかもしれませんが、実際にそうであるかどうかを確認してみましょう:
パスワードに小文字、大文字、数字を含めることができる場合、62 (26+ 26+) 10) 文字はオプションです。
8 桁のパスワードには 62^8 通りの組み合わせがあり、これは 218 兆をわずかに超えます。
1 秒あたり 10 億のハッシュ値を計算する速度に基づくと、解決にかかる時間はわずか 60 時間です。
これも非常に一般的なパスワードである 6 桁のパスワードの場合、解読にはわずか 1 分しかかかりません。 9 ~ 10 桁のパスワードを要求する方が安全かもしれませんが、ユーザーによっては面倒に感じる場合もあります。
それを解決するにはどうすればよいですか?
より遅いハッシュ関数を使用してください。
「同じハードウェア条件下で、1 秒あたり 10 億回実行できるアルゴリズムの代わりに、1 秒あたり 100 万回しか実行できないアルゴリズムを使用すると、攻撃者はブルートを実行するために 1,000 倍の時間を費やす必要がある可能性があります。
このメソッドは自分で実装できます:

コードをコピーします コードは次のとおりです。


関数 myhash($password, $unique_salt) {
$salt = "f#@V)Hu^%Hgfds";
$hash = sha1($unique_salt . $password) ;
/ / 1000 倍長くします
for ($i = 0; $i $hash = sha1($hash) return $hash;
}


BLOWFISH などの「コスト パラメーター」をサポートするアルゴリズムを使用することもできます。 PHP では、crypt() 関数を使用できます。



コードをコピー コードは次のとおりです。

function myhash( $password, $ unique_salt) {

// フグのソルトは 22 文字である必要があります
return crypt($password, '$2a$10.$unique_salt')


this 関数の 2 番目のパラメーターには、「$」記号で区切られた複数の値が含まれます。最初の値は「$2a」で、BLOWFISH アルゴリズムを使用する必要があることを示します。 2 番目のパラメーター「$10」はここではコスト パラメーターであり、底 2 の対数で、計算ループの反復数 (10 => 2^10 = 1024) を示し、値は 04 ~ 31 です。
例:




コードをコピー

コードは次のとおりです: function myhash($password, $unique_salt) {

return crypt($password, '$2a$10.$unique_salt');

}
function unique_salt() {
return substr(sha1(mt_rand()),0,22);
}
$password = "verysecret";
echo myhash($password, unique_salt());
// 結果: $2a$10$dfda807d832b094184faeu1elwhtR2Xhtuvs3R9J1nfRGBCudCCzC


結果ハッシュ値には、$2a アルゴリズム、コスト パラメーター $10、および使用した 22 ビット干渉文字列が含まれます。残りは計算されたハッシュ値です。 テスト プログラムを実行してみます。



コードをコピーします。

コードは次のとおりです。 // これはデータベースから取得されたものであると仮定します

$hash = '$2a$10$dfda807d832b094184faeu1elwhtR2Xhtuvs3R9J1nfRGBCudCCzC';

// これはユーザーが再度ログインするために入力したパスワードであると仮定します
$password = "verysecret";
if (check_password($hash, $password)) {
echo "アクセスが許可されました!";
} else {
echo "アクセスが拒否されました!";
関数 check_password ( $hash, $password) {
// 最初の 29 文字にはアルゴリズム、コスト、ソルトが含まれます
// $full_salt と呼びます
$full_salt = substr($hash, 0, 29); > // $password でハッシュ関数を実行します
$new_hash = crypt($password, $full_salt);
// true または false を返します
return ($hash == $new_hash);


実行すると、「Access Granted!」が表示されます。

8. 統合します。

上記の議論に基づいて、次のツール クラスを作成しました。



コードをコピー
コードは次のとおりです:


class PassHash {
// フグ
private static $algo = '$2a';
// コストパラメータ
private static $cost = '$10';
// 主に内部使用用
public static function unique_salt() {
return substr(sha1(mt_rand()),0,22);
}
// これはハッシュの生成に使用されます
public static function hash($password) {
return crypt($password,
self::$algo .
self::$cost .
'$'.self::unique_salt());
}
// これはパスワードとハッシュを比較するために使用されます
public static function check_password($hash, $password) {
$full_salt = substr($hash, 0, 29) ;
$new_hash = crypt($password, $full_salt);
return ($hash == $new_hash);
}
}


以下は注册時の使用法:

复制代 代码如下:


// クラス
require ("PassHash.php") を含めます。
// $_POST からすべてのフォーム入力を読み取ります
// ...
// 通常のフォーム検証処理を実行します
// ...
// パスワードをハッシュします
$pass_hash = PassHash::hash($_POST['パスワード']);
// $_POST['password'] を除くすべてのユーザー情報を DB に保存します
// 代わりに $pass_hash を保存します
// ...


以下は登录です時の使用法:

复制代 代码如下:


// クラス
require ("PassHash.php) を含めます");
// $_POST からすべてのフォーム入力を読み取ります
// ...
// $_POST['username'] または同様のものに基づいてユーザー レコードを取得します
// ...
// ユーザーがログインしようとしたパスワードを
if (PassHash::check_password($user['pass_hash'], $_POST['password']) {
// アクセスを許可
/ / ...
} else {
// アクセスを拒否
// ...
}


9.加密かどうか使用可能
并不是全システム统都支持Blowfish加密算法,虽然它现在已经很普及了,你可用以下代码来检查你的システム统支持否:

复制代 代码如下:


if (CRYPT_BLOWFISH == 1) {
echo "Yes";
} else {
echo "No"; 🎜>

は、この計算法が内部に組み込まれているため、php5.3 ではこの点を考慮する必要はありません。パスワードは、ほとんどの Web アプリケーション プログラムに対して十分に安全になっています。また、ユーザーは、最小ビット数を必要とする、文字、数字、および特殊文字の混合暗号などを使用することもできます。




関連ラベル:
ソース:php.cn
このウェブサイトの声明
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。
人気のおすすめ
人気のチュートリアル
詳細>
最新のダウンロード
詳細>
ウェブエフェクト
公式サイト
サイト素材
フロントエンドテンプレート