web-dev-qa-db-ja.com

以前のログインサーバーを壊すことなく、ソルトハッシュをユーザー認証に追加する方法はありますか

次のシナリオが表示されます。

ユーザーのテーブルを含むMySQL-Databaseがあります。テーブルには2つのフィールドがあります:usernamepassword。パスワードは無塩ハッシュとして保存されます。

15年以上前のアプリケーションは、このデータベースを使用して、サービスへのアクセスを認証します。内部からのみアクセスできます。

私たちのチームは、過去15年間に学んだベストプラクティスと教訓に従って新しいアプリケーションを提供することにより、これらのサービスを最新化することを求められています(元のアプリケーションでは無視された可能性があります)。このアプリケーションは、広く開かれたインターネットからアクセスできるものとします。

現在のデータベースを認証の目的で再利用して、両方のアプリケーションを基礎となる同じ認証データベース上で並行して実行できるようにすることになっています。

このアプリケーションは同僚や私に関する個人情報の取り扱いに関するものであるため、セキュリティに関していくつかの懸念を表明しました。

最新化の一環として、パスワードポリシーを導入し、パスワードをソルトとペッパーの両方で保存することにしました。

これは、データベーステーブルが3番目のフィールドsaltを取得し、passwordフィールドがソルトおよびペッパーハッシュを格納することを意味します。

ここでの問題は、これが古いアプリケーションの認証を破壊することです。コードは非常にレガシーであり、動作するコンパイラもないため、元のアプリケーションのコードを変更することは問題外です。

したがって、私の質問は次のとおりです。

  • 古いアプリケーションのユーザー認証機能を維持しながら、ソルト(およびペッパー)を認証データベースに追加する安全な方法はありますか?古いアプリケーションは本質的に安全ではありませんが、イントラネットの外部からはアクセスできませんですが、新しいアプリケーションはそうであることを覚えておいてください。
30
Ben

ここで競合する要件があります。互換性の要件により、古いハッシュを保持する必要があります。セキュリティ要件により、それらを削除する必要があります。ここでは、どの要件を満たす必要があるかを選択する必要があります。

下位互換性を維持する場合は、悪い状況を最大限に活用してください。

  • 古いハッシュと新しいハッシュは別のテーブルに保存する必要があり、Webアプリケーションが使用するデータベースユーザーには古いハッシュへの読み取りアクセス権がありません。これには、テーブルや列の権限を使用してください。
  • 古いアプリケーションが不要になったらすぐに、古いハッシュのテーブルを削除します。 Ashley Madisonはこの時点で有名に失敗しました-彼らはbcryptにアップグレードしました、そして、いくつかのばかげた理由のために、彼らは古いMD5ハッシュをデータベースに置いたままにしました。データベースがリークされたとき、その派手なbcryptはあまり役に立ちませんでした...

または、少し混乱することを恐れない場合:

  • 古いハッシュをドロップします。新しいアプリケーションで、オプション「古いアプリケーションの一時パスワードを作成する」を追加します。古い方法でハッシュされ、データベースにX分間のみ保持される、ランダムな長いパスワードを提供します。ユーザーは古いアプリケーションにログインでき、パスワードは自動的に削除されます。
50
Anders

古いアプリケーションのユーザー認証機能を維持しながら、ソルト(およびペッパー)を認証データベースに追加する安全な方法はありますか?

はい、できます。以下は、高レベルの実装手順です。基本的な手法は、すべてのパスワードをハッシュしてから、クライアントとレガシーサーバー間の接続をMITMして、ハッシュされていないパスワードをハッシュされたパスワードに置き換えることです。展開計画を立てる必要があることに注意してください。本番で盲目的にステップ1を実行すると、すべてが壊れます。

ステップ1:既存のすべてのパスワードをソルトし、ソルトをどこかに保存します。レガシーデータベースのパスワードフィールドをソルトパスワードで上書きします。

ステップ2:シムを作成します。シムは、レガシーAPIと同じパラメーターを受け入れます。
つまり、レガシーAPIが次のように実装されている場合:

LegacyDoStuff(username,password,argument)
{
    if(!VerifyCredentials(username,password)) return AuthenticationError();
    result = DoStuff(argument);
    return result;
}

新しいAPIは次のように実装されます。

NewDoStuff(username,password,argument)
{
    hashedpassword = DoHash(password+getSalt(username))
    return LegacyDoStuff(username,hashedpassword,argument);
}

手順3:レガシークライアントをメインサーバーではなくシムに向けます(または、同等に、レガシーサーバーを新しいIP/DNSに移動し、シムを古いIP/DNSに置きます)。

このアプローチでは、レガシーコードの内部をブラックボックスとして扱うことができますが、シムはクライアントとクライアントの間でリクエスト/レスポンスを送信する必要があるため、レガシーコードのパブリックサーフェス領域を認識する必要があります。レガシーサーバー。

このアプローチは、他の回答で説明されているアプローチとは異なり、古いパスワードの保存を完全に回避します。ただし、このアプローチは実行がはるかに困難であり、バグが発生する可能性がはるかに高くなります。

24
Brian

古いアプリケーションのユーザー認証機能を維持しながら、ソルト(およびペッパー)を認証データベースに追加する安全な方法はありますか?

いいえ。パスワードをソルトおよびハッシュする理由は、ユーザーデータベースがハッキング/リーク/侵害された場合、攻撃者がユーザーのパスワードにアクセスできないようにするためです( を参照してください)ハッシュのポイントとはパスワード? )。説明するソリューションでは、ユーザーのパスワードは引き続き、ユーザーデータベースの隣接する列にプレーンテキストで格納されます。これは、パスワードをソルトおよびハッシュする目的を完全に無効にします。

11
mti2935

明らかに、古いアプリケーションが機能できるように、現在のテーブルを残す必要があります。

新しいアプリケーションで使用するために、salted + pepperedハッシュを含む新しいテーブルを追加します。

  • 古いアプリケーションから新しいアプリケーションにアクセスできないようにします。
  • ユーザーのパスワードを更新するときに両方のテーブルを更新する
  • 新しいシステムのすべてのユーザーにパスワードの更新/リセットを要求する
  • 新しいテーブルは古いハッシュのコピーを最初に保存でき、ユーザーがパスワードを更新するときに適切にハッシュされるように更新します。

これは、パスワード制御システムがいずれかのコンテンツシステムから分離されていることを前提としています。

これは、新しいシステムの脆弱性が(移行後)古いハッシュにアクセスできないことを意味します。データベースに脆弱性が存在する可能性があるため、新しいシステムのデータベース認証情報がアクセスできないテーブルにアクセスできますが、新しいアプリケーションに脆弱性が存在する可能性ははるかに低くなります。

7
Douglas Leeder
  1. 2つの新しい列、salt列、および新しいハッシュ列を追加します。最初はnullです。
  2. 認証要求が来たら、ソルトフィールドを確認してください。
  3. フィールドに値がある場合、ペッパーとソルトが追加された新しいスタイルのハッシュがあります。それに応じて処理します。
  4. そうでない場合、これは古いスタイルのハッシュです。古いメカニズムを使用して確認します。成功すると想定すると、元のリクエストのプレーンテキストのパスワードが得られます。ソルトを生成して保存すると、新しいスタイルのハッシュを計算して保存するために必要なものがすべて揃います。レガシーシステムはこれを実行することを認識しないため、新しいシステムのみが実行できます。 (どちらのシステムもそれを処理する必要はないが、データベース自体にジョブを委任できれば、より良かったでしょう。新しいシステムはできますが、古いシステムはできません。)

最終的には、全員が少なくとも1回ログインしてハッシュを透過的に更新したか(テーブルにNULLソルトが残っていない)、またはまったくログインしていない人だけが残っています。

無塩ハッシュと塩漬けハッシュを表のすぐ隣に置くことは、レガシーシステムが引き続き使用されている限り、新しいシステムでのベストプラクティスの使用はセキュリティの観点からは無関係であることを示しているにすぎません。

5
Hayley

はい、これは可能です。

新しいアプリケーションで、ハッシュのハッシュダブルハッシュアルゴリズムを使用します。例:step1:ソルトされたパスワードをハッシュします。step2:そのハッシュをハッシュします(ソルトなし)。

アプローチ#1:

パスワードを取得してユーザーパスワードのソルトハッシュを生成し、画面に表示するか、クリップボードに無音で表示するシンプルなスタンドアロンアプリを開発します。

すべての内部ユーザーに、古いアプリケーションのパスワードをスタンドアロンアプリの出力にリセットするように依頼します。これから、ログイン時にスタンドアロンアプリにパスワードを入力するプロセスを使用して、ログインに入力されるパスワードハッシュを生成します。画面。視覚的な手がかりを避けるために、出力をクリップボードに生成できます。

アプローチ#2

どういうわけかdbを使用できる場合は、データベースでハッシュのハッシュを実行します。ステップ2をソルトハッシュにして、それだけを保存します。

明確にするために編集します。例:カスタム演算子をサポートするDBを使用している場合、newsaltedhash(oldhash)をカスタムタイプとして格納する列でカスタム等価演算子を宣言します。アプリが一致するユーザー名とoldhash、カスタムオペレーターは受信ハッシュに対してnewsaltedhashを実行して比較を行います。パスワードのリセットは挿入トリガーによって処理されます。

概要:どちらのシナリオでも、アプリは最初に設計されたとおりに機能し、保存されているパスワードハッシュはハッシュ+ソルトダブルハッシュです(Pepperはオプションでいずれかの実装に追加されます)。

2
Frank

この答えは ブライアンの答えフランクのアプローチ#1 に触発され、どちらも基本的に古いアプリケーションのパスワードとして新しいソルトハッシュを使用します。これには、アプリケーションがデータベースからソルトを取得してハッシュを生成する必要があるため、そのアプリケーションでユーザーを認証し、完全に別のランダムトークンを古いシステムのパスワードとして使用する方が簡単で安全です。

ステップ1:最新のパスワードハッシュソリューションを選択する

安全なソルトの生成を処理する必要のある、選択した言語のサポートされているパスワードハッシュライブラリを選択してください。おそらく、パスワードを検証するために必要なハッシュ、ソルト、およびその他のパラメータを含む単一の文字列を出力し、将来、より強力なアルゴリズムを段階的に展開できるようにします。たとえば、PHPの場合は、 組み込みのパスワードハッシュ関数 を使用します。

データベースに新しい列を作成して、この単一の文字列を格納します。これを最初に入力する1つの方法は、NewHashFunction(OldStoredHash)を計算してから、NewVerifyFunction(NewStoredHash, OldHashFunction(UserEnteredPassword))を実行することです。これに関する既存の議論はたくさんありますが、この回答の残りの部分には違いはありません。

手順2:古いパスワードの列を空白にする

安全でない古いハッシュをすべて完全にワイプしますが、列は落とさないでください。この時点では、誰も古いアプリケーションにログインできないため、修正する必要があります...

ステップ3:古いアプリケーションにログインするためのツールを構築する

新しいアプリケーション(ユーザーが既に認証されている場合)またはスタンドアロンページ(新しいパスワードライブラリを使用してユーザーを認証する場合)でページを作成します。

  • 長いランダムパスワードを生成します
  • 認証されたユーザーの古いパスワード列に、このランダムパスワードの無塩ハッシュを格納します
  • ユーザーにパスワードを表示して古いアプリケーションに入力するか、直接送信してユーザーを自動的にログインさせる

このランダムなパスワードは、どこに保存する必要もないことに注意してください。ユーザーが再度ログインする場合は、新しいパスワードを生成するだけです。

手順4:トークンをタイムアウトして、セキュリティを強化する

ランダムなパスワードは、パスワードマネージャーを使用しない限り、ほとんどのユーザーが設定するよりもはるかに強力なものになりますが、ソルトなしで、おそらく高速ハッシュを使用して保存されます。また、ユーザーに表示されたときにパスワードがどこかにコピーされるリスクもあります。ランダムなパスワードを短期間だけ有効にすることで、両方の攻撃をさらに困難にすることができます。

「ランダムパスワードの有効期限」の列をDBに追加します。これは、自動的にログインする場合は非常に短く、ユーザーが自分でパスワードを入力する必要がある場合は数分です。その後、スケジュールされたジョブは1分に1回実行され、期限切れになったすべての行の古いパスワードフィールドを空白にします。

1
IMSoP