62

発見したばかりのWebアプリを継承し、SQLServerデータベースに300,000を超えるユーザー名/パスワードをプレーンテキストで格納しています。これはVeryBadThing™だと思います。

ログインとパスワードの更新プロセスを暗号化/復号化するために更新する必要があり、システムの他の部分への影響を最小限に抑える必要があることを知っているので、データベースからプレーンテキストのパスワードを削除する最良の方法として何をお勧めしますか?

どんな助けでも大歓迎です。

編集:申し訳ありませんが、不明な点がある場合は、特定の暗号化/ハッシュ方法ではなく、パスワードを暗号化/ハッシュする手順を尋ねるつもりでした。

私はただ:

  1. DBのバックアップを作成します
  2. ログイン/パスワードコードの更新
  3. 数時間後、usersテーブルのすべてのレコードを調べて、パスワードをハッシュし、それぞれを置き換えます
  4. ユーザーが引き続きパスワードをログイン/更新できることを確認するためのテスト

私の懸念はユーザーの数の多さによるものだと思うので、これを正しく行っていることを確認したいと思います。

4

15 に答える 15

49

編集 (2016): Argon2scryptbcrypt、またはPBKDF2を優先順に使用します。状況に応じて可能な限り大きな減速係数を使用してください。精査された既存の実装を使用します。適切なソルトを使用していることを確認してください (ただし、使用しているライブラリはこれを確認する必要があります)。


パスワードをハッシュするときは、DO NOT USE PLAIN MD5を使用します。

PBKDF2を使用します。これは基本的に、ランダム ソルトを使用してレインボー テーブル攻撃を防ぎ、ハッシュを遅くするのに十分な回数反復 (再ハッシュ) することを意味します。多数の異なるパスワードが表示されます

ドキュメントから:

  • 少なくとも 1000 回、できればそれ以上の反復を実行して、実行可能な反復回数を確認してください。
  • 8 バイト (64 ビット) のソルトで十分であり、ランダムは安全である必要はありません (ソルトは暗号化されていないため、誰かが推測する心配はありません)。
  • ハッシュするときにソルトを適用する良い方法は、パスワードを HMAC キーとして使用し、ソルトをハッシュするテキストとして使用して、お気に入りのハッシュ アルゴリズムで HMAC を使用することです (ドキュメントのこのセクションを参照してください)。

セキュア ハッシュとして SHA-256 を使用した Python での実装例:

EDIT : Eli Collins が述べたように、これは PBKDF2 実装ではありません。PassLibなど、標準に準拠した実装を優先する必要があります。

from hashlib import sha256
from hmac import HMAC
import random

def random_bytes(num_bytes):
  return "".join(chr(random.randrange(256)) for i in xrange(num_bytes))

def pbkdf_sha256(password, salt, iterations):
  result = password
  for i in xrange(iterations):
    result = HMAC(result, salt, sha256).digest() # use HMAC to apply the salt
  return result

NUM_ITERATIONS = 5000
def hash_password(plain_password):
  salt = random_bytes(8) # 64 bits
  
  hashed_password = pbkdf_sha256(plain_password, salt, NUM_ITERATIONS)

  # return the salt and hashed password, encoded in base64 and split with ","
  return salt.encode("base64").strip() + "," + hashed_password.encode("base64").strip()

def check_password(saved_password_entry, plain_password):
  salt, hashed_password = saved_password_entry.split(",")
  salt = salt.decode("base64")
  hashed_password = hashed_password.decode("base64")

  return hashed_password == pbkdf_sha256(plain_password, salt, NUM_ITERATIONS)

password_entry = hash_password("mysecret")
print password_entry # will print, for example: 8Y1ZO8Y1pi4=,r7Acg5iRiZ/x4QwFLhPMjASESxesoIcdJRSDkqWYfaA=
check_password(password_entry, "mysecret") # returns True
于 2008-11-13T19:02:27.147 に答える
38

基本的な戦略は、キー派生関数を使用して、パスワードをソルトで「ハッシュ」することです。ソルトとハッシュ結果はデー​​タベースに保存されます。ユーザーがパスワードを入力すると、ソルトとその入力が同じ方法でハッシュされ、保存されている値と比較されます。それらが一致する場合、ユーザーは認証されます。

悪魔は細部に宿る。まず、選択したハッシュ アルゴリズムに大きく依存します。ハッシュベースのメッセージ認証コードに基づく PBKDF2 のようなキー導出アルゴリズムにより、特定の出力 (攻撃者がデータベースで見つけたもの) を生成する入力 (この場合はパスワード) を見つけることが「計算上不可能」になります。 )。

事前計算された辞書攻撃は、ハッシュ出力からパスワードまで、事前計算されたインデックスまたは辞書を使用します。ハッシュは遅い (またはとにかく遅いはず) ため、攻撃者は可能性のあるすべてのパスワードを 1 回ハッシュし、ハッシュを指定して、対応するパスワードを検索できるような方法でインデックス付けされた結果を格納します。これは、空間と時間の古典的なトレードオフです。パスワード リストは巨大になる可能性があるため、(レインボー テーブルのように) トレードオフを調整する方法があり、攻撃者は多くのスペースを節約するために速度を少し犠牲にすることができます。

事前計算攻撃は、「暗号化ソルト」を使用して阻止されます。これは、パスワードでハッシュ化されたデータです。秘密である必要はありません。特定のパスワードに対して予測不可能である必要があるだけです。ソルトの値ごとに、攻撃者は新しい辞書を必要とします。1 バイトのソルトを使用する場合、攻撃者は、それぞれが異なるソルトで生成された辞書の 256 のコピーを必要とします。最初に、彼はソルトを使用して正しい辞書を検索し、次にハッシュ出力を使用して使用可能なパスワードを検索しました。しかし、4 バイト追加するとどうなるでしょうか。現在、彼はその辞書を 40 億部必要としています。十分な大きさのソルトを使用することで、辞書攻撃が排除されます。実際には、暗号品質の乱数ジェネレーターからの 8 ~ 16 バイトのデータが適切なソルトになります。

攻撃者は事前計算を行わないため、試行ごとにハッシュを計算する必要があります。パスワードを見つけるのにかかる時間は、候補をハッシュするのにかかる時間に完全に依存します。この時間は、ハッシュ関数の反復によって増加します。反復回数は、通常、キー派生関数のパラメーターです。現在、多くのモバイル デバイスは 10,000 ~ 20,000 回の反復を使用していますが、サーバーは 100,000 回以上使用する場合があります。(bcrypt アルゴリズムでは、「コスト ファクター」という用語が使用されます。これは、必要な時間の対数尺度です。)

于 2008-11-13T18:13:02.753 に答える
19

暗号化されたパスワードの列をデータベースに追加してから、現在のパスワードを取得して暗号化するすべてのレコードに対してバッチジョブを実行する必要があると思います(他の人がmd5のようなハッシュはかなり標準的な編集であると述べていますが、すべきではありません)単独で使用する-良い議論については他の回答を参照してください)、それを新しい列に保存し、すべてがスムーズに行われたことを確認します。

次に、フロントエンドを更新して、ログイン時にユーザーが入力したパスワードをハッシュし、plaintext-vs-plaintextをチェックするのではなく、保存されているハッシュと比較する必要があります。

最終的にプレーンテキストのパスワードをすべて削除する前に、両方の列をしばらくそのままにして、問題が発生していないことを確認するのが賢明だと思います。

また、パスワードにアクセスするたびに、パスワードの変更やリマインダーのリクエストなど、コードを変更する必要があることも忘れないでください。もちろん、忘れたパスワードを電子メールで送信する機能は失われますが、これは悪いことではありません。代わりに、パスワードリセットシステムを使用する必要があります。

編集:最後のポイントとして、テストベッドの安全なログインWebサイトでの最初の試行で発生したエラーを回避することを検討することをお勧めします。

ユーザーパスワードを処理するときは、ハッシュが行われる場所を考慮してください。私の場合、ハッシュはWebサーバーで実行されているPHPコードによって計算されましたが、パスワードはユーザーのマシンからプレーンテキストでページに送信されました。とにかくhttpsシステム(uniネットワーク)の中にあったので、これは私が働いていた環境では大丈夫でした。しかし、現実の世界では、パスワードがユーザーシステムを離れる前に、javascriptなどを使用してハッシュし、そのハッシュをサイトに送信したいと思うでしょう。

于 2008-11-13T17:07:42.340 に答える
4

Xan のアドバイスに従って、現在のパスワードの列をしばらく保持しておくことで、問題が発生した場合にすばやく簡単にロールバックできます。

パスワードを暗号化する限り:

  • 塩を使う
  • パスワード用のハッシュ アルゴリズムを使用する (つまり、遅い)

詳細については、Thomas Ptacek の「レインボー テーブルで十分: 安全なパスワード スキームについて知っておくべきこと」を参照してください。

于 2008-11-13T17:18:31.927 に答える
3

それは数週間前の私の問題でした。私たちは大規模な MIS プロジェクトを 975 の異なる地理的な場所に展開していました。そこでは、独自のユーザー資格情報ストアが、実装済みおよび使用中のさまざまなアプリケーションの認証システムとして使用されます。すでに REST ベースと SOAP ベースの両方の認証サービスを提供していましたが、顧客は、関連するテーブルまたはビューの読み取り専用ビューへの DB 接続だけで、他のアプリケーションからユーザー資格情報ストアに到達できると主張しました。ため息... (この高度に結合された悪い設計上の決定は、別の質問の対象です)。

そのため、ソルト化され、反復的にハッシュ化されたパスワード ストレージ スキームを座って仕様に変換し、簡単に統合できるようにいくつかの異なる言語の実装を提供する必要がありました。

私たちはこれを Fairly Secure Hashed Passwords または略してFSHPと呼びました。Python、Ruby、PHP5 で実装し、Public Domain にリリースしました。http://github.com/bdd/fshpの GitHub で、消費、フォーク、フレーム化、または吐き出すことができます

FSHP はソルト化され、反復的にハッシュされたパスワード ハッシュの実装です。

設計原理は、RFC 2898 の PBKDF1仕様(別名: PKCS #5: パスワードベースの暗号化仕様バージョン 2.0)と 似ています。 (256、384、512)。すべての出力の先頭にある自己定義のメタ プレフィックスにより、コンシューマが独自のパスワード ストレージ セキュリティ ベースラインを選択できるようにしながら、ポータブルになります。

セキュリティ:

デフォルトの FSHP1 は 8 バイトのソルトを使用し、SHA-256 ハッシュを 4096 回繰り返します。- 8 バイトのソルトは、必要なスペースを 2^64 で乗算することにより、レインボー テーブル攻撃を非現実的にします。- 4096 回の反復により、ブルート フォース攻撃のコストがかなり高くなります。- このリリースの時点で 2^128 操作未満の計算量で衝突を検出する SHA-256 に対する既知の攻撃はありません。

実装:

  • Python: 2.3.5 (w/hashlib)、2.5.1、2.6.1 でテスト済み
  • Ruby : 1.8.6 でテスト済み
  • PHP5 : 5.2.6 でテスト済み

不足している言語の実装を作成したり、現在のものを改良したりすることは、誰でも大歓迎です。

基本操作(Python を使用) :

>>> fsh = fshp.crypt('OrpheanBeholderScryDoubt')
>>> print fsh
{FSHP1|8|4096}GVSUFDAjdh0vBosn1GUhzGLHP7BmkbCZVH/3TQqGIjADXpc+6NCg3g==
>>> fshp.validate('OrpheanBeholderScryDoubt', fsh)
True

クリプトのカスタマイズ:

パスワードハッシュスキームを弱めましょう。- ソルトの長さをデフォルトの 8 から 2 に減らします。 - 反復ラウンドをデフォルトの 4096 から 10 に減らします。 - 基礎となるハッシュ アルゴリズムとして SHA-1 で FSHP0 を選択します。

>>> fsh = fshp.crypt('ExecuteOrder66', saltlen=2, rounds=10, variant=0)
>>> print fsh
{FSHP0|2|10}Nge7yRT/vueEGVFPIxcDjiaHQGFQaQ==
于 2009-01-02T17:00:52.237 に答える
3

次のことを行う必要があると思います。

  1. HASHED_PASSWORD などの新しい列を作成します。
  2. 両方の列をチェックするようにコードを変更します。
  3. パスワードをハッシュされていないテーブルからハッシュされたテーブルに徐々に移行します。たとえば、ユーザーがログインすると、パスワードが自動的にハッシュされた列に移行され、ハッシュされていないバージョンが削除されます。新しく登録されたすべてのユーザーのパスワードはハッシュ化されます。
  4. 営業時間後に、一度に n ユーザーを移行するスクリプトを実行できます
  5. ハッシュ化されていないパスワードがなくなったら、古いパスワード列を削除できます (使用しているデータベースによっては、削除できない場合があります)。また、コードを削除して古いパスワードを処理することもできます。
  6. あなたは終わった!
于 2008-11-13T17:19:31.817 に答える
2

他の人が言ったように、あなたがそれを助けることができればあなたは解読したくありません。標準的なベストプラクティスは、一方向ハッシュを使用して暗号化し、ユーザーがログインしたときにパスワードをハッシュして比較することです。

それ以外の場合は、強力な暗号化を使用して暗号化してから復号化する必要があります。政治的な理由が強い場合にのみこれをお勧めします(たとえば、ユーザーがヘルプデスクに電話してパスワードを取得できることに慣れていて、パスワードを変更しないように上から強い圧力がかかっている場合など)。その場合、私は暗号化から始めて、ハッシュに移行するためのビジネスケースの構築を開始します。

于 2008-11-13T17:06:52.187 に答える
2

認証のために、可逆暗号化を使用してパスワードを保存することは避けてください。つまり、パスワード ハッシュのみを保存し、保存したハッシュに対してユーザー提供のパスワードのハッシュをチェックする必要があります。ただし、このアプローチには欠点があります。攻撃者がパスワード ストア データベースを入手した場合、レインボー テーブル攻撃に対して脆弱です。

あなたがすべきことは、事前に選択された(そして秘密の)ソルト値とパスワードのハッシュを保存することです。つまり、ソルトとパスワードを連結し、結果をハッシュして、このハッシュを保存します。認証するときも同じことを行います - あなたのソルト値とユーザー提供のパスワード、ハッシュを連結し、等しいかどうかをチェックします。これにより、レインボー テーブル攻撃が実行不可能になります。

もちろん、ユーザーがネットワーク経由でパスワードを送信する場合 (たとえば、Web またはクライアント サーバー アプリケーションで作業している場合)、パスワードをクリア テキストで送信するべきではありません。 password) を保存して hash(salt + hash(password)) に対してチェックし、クライアントにユーザー提供のパスワードを事前にハッシュして、それをネットワーク経由で送信させる必要があります。これにより、ユーザー (多くのユーザーがそうであるように) が複数の目的で同じパスワードを再利用した場合に、ユーザーのパスワードも保護されます。

于 2008-11-13T17:09:19.187 に答える
1
  • MD5などを使用して暗号化し、16進文字列としてエンコードします
  • 塩が必要です。あなたの場合、ユーザー名をソルトとして使用できます(一意である必要があり、ユーザー名は利用可能な最も一意の値でなければなりません;-)
  • 古いパスワード フィールドを使用して MD5 を保存しますが、古い (プレーン テキストの) パスワードと新しい (MD5) パスワードが共存できるように、MD5 にタグを付けます (例: "MD5:687A878...")。
  • ログイン手順を変更して、MD5 がある場合は MD5 に対して検証し、それ以外の場合はプレーン パスワードに対して検証します
  • 「パスワードの変更」および「新しいユーザー」機能を変更して、MD5 化されたパスワードのみを作成します。
  • これで変換バッチ ジョブを実行できますが、必要なだけ時間がかかる場合があります
  • 変換が実行された後、legacy-support を削除します
于 2008-11-13T17:21:33.560 に答える
1

ステップ 1: 暗号化されたフィールドをデータベースに追加する

ステップ 2: パスワードが変更されたときに両方のフィールドが更新されるようにコードを変更しますが、ログインにはまだ古いフィールドが使用されます。

ステップ 3: スクリプトを実行して、すべての新しいフィールドに入力します。

ステップ 4: ログイン時に新しいフィールドを使用し、パスワードを変更すると古いフィールドが更新されないようにコードを変更します。

ステップ 5: 暗号化されていないパスワードをデータベースから削除します。

これにより、エンド ユーザーを中断することなく、切り替えを完了できるはずです。

また、新しいデータベース フィールドに、「LastSessionID」などのパスワードとはまったく関係のない名前を付けるか、同様に退屈な名前を付けます。次に、パスワード フィールドを削除する代わりに、ランダム データのハッシュを入力します。次に、データベースが危険にさらされた場合、「パスワード」フィールドの解読に必要な時間を費やすことができます.

これは実際には何も達成しないかもしれませんが、誰かがそこに座って価値のない情報を理解しようとしていると考えるのは楽しいことです

于 2008-11-14T17:25:52.210 に答える
0

Orip によって投稿された素晴らしい python の例に 1 つの改善を提案したいと思います。random_bytes関数を次のように再定義します。

def random_bytes(num_bytes):
    return os.urandom(num_bytes)

もちろん、osモジュールをインポートする必要があります。このos.urandom関数は、暗号化アプリケーションで安全に使用できるランダムなバイト シーケンスを提供します。詳細については、この関数のリファレンス ヘルプを参照してください。

于 2009-04-22T17:37:01.860 に答える
0

MD5 と SHA1 には少し弱点があるため (2 つの単語が同じハッシュになる可能性があります)、SHA256-SHA512 / 反復ハッシュを使用してパスワードをハッシュすることをお勧めします。

アプリケーションが書かれている言語で小さなプログラムを書き、各ユーザーに固有のランダムなソルトとパスワードのハッシュを生成します。私が検証と同じ言語を使用する傾向がある理由は、暗号化ライブラリが異なると処理が少し異なるため (パディングなど)、同じライブラリを使用してハッシュを生成し、それを検証してそのリスクを排除するためです。このアプリケーションは、平文のパスワードをまだ知っているので、必要に応じて、テーブルが更新された後にログインを検証することもできます。

  1. MD5/SHA1 を使用しない
  2. 適切なランダム ソルトを生成する (多くの暗号ライブラリにはソルト ジェネレーターがあります)
  3. orip が推奨する反復ハッシュ アルゴリズム
  4. パスワードがネットワーク上で平文で送信されないようにする
于 2008-11-14T16:45:32.493 に答える
0

すべてのセキュリティ上の決定と同様に、トレードオフがあります。おそらく最も簡単な方法であるパスワードをハッシュする場合、元のパスワードを返すパスワード取得機能を提供することも、スタッフがアカウントにアクセスするためにその人のパスワードを調べることもできません。

独自のセキュリティ上の欠点がある対称暗号化を使用できます。(サーバーが侵害された場合、対称暗号化キーも侵害される可能性があります)。

公開鍵暗号化を使用して、Web アプリケーションから分離された秘密鍵を格納する別のマシンでパスワード取得/カスタマー サービスを実行できます。これは最も安全ですが、2 台のマシン アーキテクチャが必要であり、その間におそらくファイアウォールが必要です。

于 2008-11-13T17:10:40.633 に答える
-1

パスワードをハッシュするには、HashBytes関数を使用できます。varbinaryを返すため、新しい列を作成してから、古いvarchar列を削除する必要があります。

好き

ALTER TABLE users ADD COLUMN hashedPassword varbinary(max);
ALTER TABLE users ADD COLUMN salt char(10);
--Generate random salts and update the column, after that
UPDATE users SET hashedPassword = HashBytes('SHA1',salt + '|' + password);

次に、次のようなクエリを使用して、パスワードを検証するようにコードを変更します

SELECT count(*) from users WHERE hashedPassword = 
HashBytes('SHA1',salt + '|' + <password>)

ここで、<password>はユーザーが入力した値です。

于 2008-11-13T17:07:43.230 に答える
-1

I'm not a security expert, but i htink the current recommendation is to use bcrypt/blowfish or a SHA-2 variant, not MD5 / SHA1.

Probably you need to think in terms of a full security audit, too

于 2008-11-13T18:05:08.543 に答える