web-dev-qa-db-ja.com

パスワードのログインにsaltを実装するにはどうすればよいですか?

ログインシステムにsaltを実装したいのですが、これがどのように機能するかについて少し混乱しています。その背後にある論理が理解できません。 md5は一方向のアルゴリズムであり、私が遭遇したすべての関数はすべてを一緒にハッシュしているように見えることを理解しています。この場合、比較のためにパスワードをどのように取り戻すのですか?私の最大の質問は、単にパスワードをハッシュするよりも、ユーザーのパスワードをソルトする方が安全なのかということです。データベースが危険にさらされた場合、ハッシュとソルトがデータベースにあります。ハッカーが必要とするのはこれだけではありませんか?

また、ここSOに別の開発者が言った別の投稿を見つけました:

「ソルトとアルゴリズムが保存されていることを確認してください別にデータベース」

塩をデータベースに保存したいのですが。私がそうするならば、これは本当に問題ですか?

これがどのように機能するか、またベストプラクティスが何であるかを理解するための助けを探しています。どんな助けでも大歓迎です。


編集:私は皆の反応とアイデアに感謝したいと思います。今はもっと混乱しているかもしれませんが、それは確かに私にとって学習経験でした。みんなありがとう。

55
Timmay

ハッシュ関数は、同じ入力文字列に対して常に同じ値を返します。私のユーザー(アリス)がパスワードsecretを持っているとしましょう。 md5() を使用してsecretをハッシュすると、次のハッシュが生成されます

_5ebe2294ecd0e0f08eab7690d2a6ee69
_

攻撃者(Mallory)は、辞書(一般的な単語とパスワードのリスト)またはそのサービスを提供するさまざまなサイトの1つを使用して、辞書で_5ebe2294ecd0e0f08eab7690d2a6ee69 = secret_を見つけたときに、パスワードが秘密であることを簡単に見つけることができます。

ハッシュする前にソルトするプロセスにより、ソルトを知らずに辞書攻撃を使用することが難しくなります。次のことを考慮してください。

_<?php
$salt = '@!#%$@#$@SADLkwod,sdaDwqksjaoidjwq@#@!';
$hash = md5($salt . 'secret');
_

結果のハッシュは_b58ad809eece17322de5024d79299f8a_になりますが、アリスのパスワードはまだsecretです。マロリーが塩漬けのハッシュを手に入れたら、辞書に答えが見つからない可能性があります。もしそうなら、辞書は彼女に間違った答えを与えるでしょう。

データベースに静的ソルトを保存しないでください。できれば、アプリケーションの構成と一緒に保存してください(ちなみに、Webからは入手できないはずです)。

動的ソルトを使用する場合は、データベースを使用する必要があります。既存の有効なデータのnull以外の列を使用して、ソルトを構築します(blowfishで暗号化された秘密暗号化キーに基づくユーザー名の文字列は通常暗号化されて安全です)。塩に別のカラムを使用しないでください。既存の列を使用できない場合は、ハッシュと同じ列にソルトを組み込みます。たとえば、128ビットのソルトには最初の32文字を使用し、160ビットのハッシュには最後の40文字を使用します。次の関数はそのようなハッシュを生成します:

_function seeded_sha1($string, $seed_bits) {
    if(($seed_bits % 8) != 0) {
        throw new Exception('bits must be divisible by 8');
    }

    $salt = '';
    for($i = 0; $i < $seed_bits; $i+=8) {
        $salt .= pack('c', mt_Rand());
    }

    $hexsalt = unpack('h*hex', $salt);

    return $hexsalt['hex'] . sha1($salt . $string);
}

function compare_seeded_sha1($plain, $hash) {
    $sha1 = substr($hash, -40);
    $salt = pack('h*', substr($hash, 0, -40));

    $plain_hash = sha1($salt . $plain);
    return ($plain_hash == $sha1);
}
_

攻撃者がSQLインジェクションを使用してデータベースに侵入した場合、攻撃者はアプリケーション構成にアクセスできないため、少なくとも攻撃者が取得したハッシュは役に立ちません。サーバーが根付いた場合、何をしてもほとんどゲームオーバーになります。

注:md5() には、他の種類の攻撃が可能です。そのためです。より安全なハッシュアルゴリズム、たとえば sha1() を使用します。または、さらに良いことに、 Portable PHPパスワードハッシュフレームワーク を使用します。これは、セキュリティを念頭に置いて設計されており、ほとんどすべてのPHPバージョン。

_require('PasswordHash.php');

$pwdHasher = new PasswordHash(8, FALSE);

// $hash is what you would store in your database
$hash = $pwdHasher->HashPassword( $password );

// $hash would be the $hashed stored in your database for this user
$checked = $pwdHasher->CheckPassword($password, $hash);
if ($checked) {
    echo 'password correct';
} else {
    echo 'wrong credentials';
}
_
19
Andrew Moore

ソルトのポイントは、攻撃者が事前に計算された レインボーテーブル を介してサイト間でブルートフォース攻撃のコストを償却するのを防ぐことです(または、ユーザーごとに異なるソルトを使用する場合:サイトのすべてのユーザー)。 =。

プレーンハッシュを使用すると、攻撃者はそのようなテーブルを1回計算し(非常に長く、コストのかかる操作)、それを使用して任意のサイトのパスワードをすばやく見つけることができます。サイトが1つの固定ソルトを使用する場合、攻撃者はそのサイト専用の新しいテーブルを計算する必要があります。サイトがユーザーごとに異なるソルトを使用している場合、攻撃者はレインボーテーブルに煩わされるのをやめることができます。攻撃者は各パスワードを個別にブルートフォースする必要があります。

この利点を得るために、塩を別々に保管する必要はありません。理論的には、辞書や短いパスワードの弱点を中和するため、さらに安全になります。実際には、1日の終わりに、パスワードを確認するためにソルトにアクセスする必要があるため、気にする価値はありませんどこか。また、それらを分離しようとすると、システムがより複雑になります。システムが複雑になるほど、セキュリティホールが発生する可能性が高くなります。

編集:私の具体的な推奨事項:

  • ユーザーごとに長い疑似ランダムソルトを生成し、DBに保存します
  • Bcryptベースのハッシュを使用する
  • 理想的には、自分で実装するのではなく、代わりに 既存のライブラリ を使用してください
31

塩の使用を忘れて(あなたが言及した理由の一部のために)、代わりにbcryptを使用してください:

良い説明については、以下を参照してください: http://codahale.com/how-to-safely-store-a-password/

15
Rich

他の答えは良いので、他の誰も言及していないマイナーな点を投げます。すべてのパスワードに同じソルトを使用する必要はありません。2人が同じパスワードを持っている場合、それらは同じハッシュを持っているからです。これは、攻撃者が悪用できる情報を公開しています。

すべてのユーザーに同じソルトを使用し、Jurajの優れたアイデアを使用して、パスワードを他の変更されないデータベースフィールド(ユーザーに固有)と組み合わせることができます。ただし、この情報はパスワードに関連付けられているため、注意してください。一意のハッシュを保証するためにユーザー名とパスワードを一緒にハッシュする場合、新しいユーザーを作成して新しいパスワードの設定を要求しない限り、ユーザー名を変更することはできません。

ユーザーごとに一意のソルトを持ち、それをパスワードハッシュと一緒に保存する例として、一般的なLinuxシステムで / etc/shadow を指摘します。

root@linux:/root# cat /etc/shadow | grep root
root:$1$oL5TTZxL$RhfGUZSbFwQN6jnX5D.Ck/:12139:0:99999:7:::

ここで、oL5TTZxLはソルトであり、RhfGUZSbFwQN6jnX5D.Ck /はハッシュ。この場合、プレーンテキストのパスワードはrootであり、私のシステムが使用するハッシュアルゴリズムはMD5ベースのBSDパスワードアルゴリズムです。 (私のシステムよりも新しいシステムのハッシュアルゴリズムは優れています)

7
indiv

比較のためにパスワードを取得することはありません。ログインを試みるときにパスワードを暗号化し、保存されている値を新しく暗号化された値と比較します。

5
brian

あなたが言ったように、ハッシュアルゴリズムは一方向でのみ機能します(またはそれらが十分に強力である場合にのみ:-D)

ソルトについての質問については、パスワードを静的ソルト文字列といくつかのデータベースからの動的データでハッシュすることをお勧めします。これは変更しない後に作成したら

これはパスワードを保存する非常に安全な方法です。データベースが侵害された場合でも、ハッカー/クラッカーは静的な文字列ハッシュを取得し、すべてのソルトをどのように適用したかを推測する必要があります。

たとえば、次の列を持つusersテーブルがあるとします。

id
username
password
created_at

idおよびcreated_at一度入力した後は、決して変更しないでください。

したがって、ユーザーのパスワードをハッシュする場合は、次のように簡単に行うことができます。

<?php
    $staticSalt = '!241@kadl;ap][';
    $userPass = 'my new pass';
    // assuming $user variable is already populated with DB data
    // we will generate new hash from columns and static salt:
    $genPass = sha1($user['id'] . $userPass . $user['created_at'] . $staticSalt);
?>

これがお役に立てば幸いです:)乾杯

2
Juraj Blahunka

パスワードのハッシュは、それらのパスワードを自分の管理者から秘密にしておくことを目的としています。

1)管理者が他のシステムにアクセスするためにパスワードを使用する場合を除いて、データベースにプレーンテキストのパスワードを保持することは問題ありません。

2)単一のグローバルソルトを使用できます。これをパスワードと組み合わせて(パスワードの先頭に追加またはXORすることにより)、データベースに保存するためにハッシュします。しかし、それは悪意のある管理者とその1つのソルト用に設計されたRainbowテーブルに対して脆弱です。

3)ユーザーごとに個別のソルトを設定できます。データベースはソルト、およびパスワードとソルトの組み合わせから派生したハッシュを格納するために使用されます。これにより、レインボー攻撃は防止されますが、ブルートフォース攻撃は引き続き可能です。

4)最後に、速度が制限されたハードウェアハッシュソリューションを使用して、ハッシュ関数を秘密にしておくことができます。

それはあなたができる限り良いことです。人間の性質上、パスワードのドメインは限られており、ブルートフォース攻撃に対して脆弱です。管理者がユーザーパスワードを取得して、アクセスしてはならない他のシステムで使用することを防止しようとしています。

その他の注意事項:

a)パスワードとソルトの組み合わせでbcryptを使用して、攻撃者のブルートフォース攻撃を遅らせることができます。ただし、管理者を想定しているため、辛抱強くなる可能性があります。

b)ソルトをパスワードハッシュから分離しておくことは効果的な防御ではありません。結局のところ、管理者を想定しています。

c)既存のデータをソルトとして使用する方が少し良いですが、既存のデータがランダムソルトと同じくらいのエントロピーを持っているとは思えません。

2
Kyle Lahnakoski

ユーザーのパスワードをソルトすることは、事前計算攻撃から保護できるため、パスワードをハッシュするよりも安全である可能性があります。

たとえば、ハッカーがデータベースにアクセスし、パスワードがソルトされていない場合、ハッカーはハッシュのデータベースでハッシュを検索できます( http://en.wikipedia.org/wiki/Rainbow_tableを参照) )元のパスワードを取得します。

1
Sam Hartsfield