web-dev-qa-db-ja.com

「Remember Me」機能を安全に実装するにはどうすればよいですか?

すべての標準ログイン情報を実装するWebサイトをすでに持っていると仮定すると、ユーザーが特定の期間(30日間としましょう)に自動的にログインできるようにする正しい最も安全な方法は何ですか?この期間は厳密に適用されます。つまり、有効な解決策はCookieに有効期限を与え、ユーザーのブラウザーがCookieを適時に削除することを望んでいるとは思いません。

既存のログインシステムの詳細:

  • ユーザーはユーザー名とパスワードを入力する必要があります
  • データベースはユーザー名とstrong_hash_function(password + user_specific_random_salt)を保存します
  • ログインフォームが読み込まれ、SSL経由で送信されます。後続のすべてのページも同様です。

そこには多くの例があり、セキュリティ上の欠陥が明らかなものも数多くあります。 PHPをターゲット言語として使用しますが、概念はどの言語にも適用できるはずです。

57
colithium

私の回答は不完全であり、重要な欠陥があります。状況によっては @ Scottの回答 を参照してください。


私の答えには2つの部分があります。

まず、脅威モデルがクライアント側のCookieの露出について心配していないと仮定して、サーバー側でナンスを生成して保存し、ユーザー名とその他の情報(クライアントIP、コンピュータ名、タイムスタンプなど)を入力し、Cookieで送信します。 nonceは有効期限と共にデータベースに保存され、Cookieが戻ってきたときに両方ともチェックされます。
これは「記憶」Cookieのみであり、セッションCookieではありません。つまり、セッションなしでCookieを取得した場合、新しい通常のセッションCookieを再発行します。また、セッションCookieと同様に、これはhttps経由でのみ配信され、secureおよびhttpOnly属性を使用する必要があります。もちろん、Cookieのスコープは適切です。

第2に、記憶されたユーザーに対しては、(おそらく、機密性に応じて)特定の機密操作に対して再認証プロセスを呼び出す必要があります。つまり、場合によっては、パスワードを再度入力する必要が生じることもありますが、それはめったになく、機密性の高い操作のためだけです。これは、CSRFや共有デスクトップの公開などの攻撃を防ぐためです。

31
AviD

私は 安全なログインと「覚えておく」チェックボックス についてかなり詳しく書いています。受け入れられた答えは間違っていませんが、ある意味では必要以上に複雑であり、もう少し複雑さが必要な領域は無視しています。

サーバー側でナンスを生成して保存し、それをユーザー名とその他の情報(クライアントIP、コンピューター名、タイムスタンプなど)でハッシュし、Cookieで送信します。 nonceは有効期限と共にデータベースに保存され、Cookieが戻ってきたときに両方ともチェックされます。

したがって、クライアントに情報を公開しない実装は次のようになります...

_// Storage:
setcookie(
    'rememberme',
    hash_hmac('sha256', $username . $_SERVER['REMOTE_ADDR'], $storedNonce),
    time() + 8640000 // 100 days, for example
);
// Validation:
$valid = hash_equals(
    $_COOKIE['rememberme'],
    hash_hmac('sha256', $username . $_SERVER['REMOTE_ADDR'], $storedNonce)
);
_

ここで明らかな制限は、IPアドレス(またはその他の情報)が変更された場合、Cookieが役に立たなくなることです。これを良いことだと考える人もいますが、Torユーザーの使いやすさに対して不必要に敵対的だと思います。

上記の引用は、貧しい人のJSON Webトークンのような別の意味を持っていると解釈することもできます。ただし、HTTP Cookieはドメインあたり4 KiBに制限されています。スペースは貴重です。

もう1つの問題:データベースクエリは、アプリケーションについてどのくらいの情報をリークしますか? (はい、私はサイドチャネルについて話しています。)


Paragon Initiativeの安全な「Remember Me」戦略

ストレージ:

  1. random_bytes()(PHP 7.0+または random_compat を介して)からランダムな9バイトの文字列を生成し、base64で12にエンコードします。これはデータベースのルックアップに使用されます。
  2. できれば18バイト以上の別のランダム文字列を生成し、もう一度base64でエンコードします(24以上に)。これは実際には認証に使用されます。
  3. _$lookup_およびhash('sha256, $validator)をデータベースに保存します。もちろん、特定のユーザーアカウントに関連付けられています。
  4. _$lookup . $validator_をユーザーのHTTP Cookieに保存します(例:rememberme)。

検証(自動ログイン):

  1. Cookieを_$lookup_と_$validator_に分割します。
  2. _$lookup_に基づいてデータベース検索を実行します。ここにタイミングサイドチャネルがあれば問題ありません。
  3. hash_equals($row['hashedValdator'], hash('sha256', $validator))を確認してください。
  4. 手順3でTRUEが返された場合は、現在のセッションを適切なユーザーアカウントに関連付けます。

セキュリティ分析:

  • 緩和されるサイドチャネルは何ですか?
    最も大幅に:データベースの検索で使用される文字列比較操作でのタイミング情報リークの影響を軽減します。

    単純なランダムトークン認証を実装した場合、攻撃者はユーザーに対して有効な認証トークンを見つけるまで、多くのリクエストを送信する可能性があります。 (おそらく、なりすましている犠牲者を選択することはできません。)

  • 攻撃者が長期認証データベーステーブルをリークできる場合はどうなりますか?
    データベースには_$validator_のSHA256ハッシュを保存しましたが、ハッシュのプレーンテキストはCookieに保存されました。入力は高いエントロピー値であるため、ブルートフォースで検索しても結果が得られる可能性はほとんどありません。 (これにより、bcryptなどの必要性も軽減されます。)

この戦略は Gatekeeper で実装されています。

10

それは興味深い質問です。まず、アプリケーションのセキュリティをいくらか弱めることなく実現できるかどうかはわかりませんが、トレードオフに値すると考えられるサイトによって異なります。

したがって、1つのアプローチは

セッション状態データベースは、トークンが設定された時刻と、提示されたトークンを比較できるようにトークンを記憶しておく必要のある期間を記録する必要があります。

ユーザーがアプリケーションにログインすると、チェックボックスがあり、ログイン状態を維持できます(ユーザーが選択できる期間の範囲が理想的です)。

セッショントークンが設定されると、その期間がCookieの有効期限として使用されます。その後のアクセスでは、Cookieがアプリケーションに提示され、サーバーはそれがまだ有効であるかどうかを確認する必要があります(つまり、remember me機能の有効期限が切れていない)。トークンがまだ有効である場合、ユーザーは認証済みとして扱われます。そうでない場合、トークンはクリアされ、ユーザーはログイン画面にリダイレクトされます。

このアプローチの問題。共有ブラウザは、不正アクセスが可能であることを意味します。

また、(サイトの脆弱性、ブラウザの問題、またはマシンに埋め込まれたマルウェアのいずれかを介して)Cookieにアクセスできるすべてのユーザーが、サイトへの不正アクセスを取得できます。これを軽減するための一般的な提案の1つは、Cookieを送信元IPアドレス(もちろん暗号化またはサーバーに保存されている)に関連付けようとすることです。多数のIPアドレスから送信され、またなりすましの影響を受ける可能性があります。

5
Rory McCune

Cookieを削除するためにブラウザーに依存する必要はありません。Cookieの有効期限をサイトに確認させる必要があります。

実際、これを安全にするために、とにかくそれを行う必要があると思います。有効期限をCookie値の一部として設定し、 Cookie自体のプレーンテキストの有効期限プロパティ。

また、Cookieを安全であるとマークし、個々のセッションの期間中はセカンダリCookieを使用するか、SSLでセッション全体(ログインプロセスだけでなく)を保護して、Cookieがネットワークから盗まれるのを防ぎます。無許可のパーティによって使用されます。

4
Xander

この記事 は、Cookieの盗難やセッションIDの推測を防ぐアプローチについて説明しています。

  • 「remember me」Cookieは、ユーザーID、トークン(大きな乱数)、およびシーケンストークン(別の大きな乱数)で構成されます。
  • ユーザーがCookieを提示すると、データベースでこれらの3つの情報が検索されます。
    • 見つかった場合、シーケンストークンが再生成され、データベースに保存され、ユーザーに送信されます。
    • ユーザー名とトークンのみが見つかった場合は、シーケンストークンがすでに使用されているため、他の誰かがCookieを盗んだと考えられます。次に、システムはユーザーに警告するか、データベースからこのユーザーのすべての保存されたトークンを削除できます。

追加のセキュリティ対策として、ユーザーが「remember me」Cookieでのみ認証された場合、ログインデータの変更や支払いなどの重要なアクションでパスワードを要求することをお勧めします。

ユーザーIDとトークンを含むデータベーステーブルには、トークンの有効期限も含まれています。また、ユーザーエージェントとIPアドレスも含まれる可能性があるため、攻撃者はこれらも複製する必要があります。トークンは基本的にパスワードのように機能し、データベースにアクセスした攻撃者がログインできるため、すべてのトークンはハッシュとしてのみ格納する必要があります。

PHPの サンプル実装 を作成しました。

0
chiborg