6

私はこのビジネス上の問題を解決しようとしています:

  • ユーザーは 5 分ごとに 10 回のログイン試行を取得します
  • ユーザーの試行回数が 10 回を超えると、「ログインを待つ必要があります」というメッセージが表示されます
  • 5 分経過したら、試行回数をリセットして、ユーザーにあと 10 回試行させる必要があります。

を使用せずにこれを行いたいのTimerですが、この情報のほとんどをSession

public class LoginExp
{
  public DateTime FirstAttempt;
  public int NumOfAttempts;
}

FirstAttemptページの読み込み時に DateTimeを保存します。

[ログイン] ボタンをクリックします。

  • 私はインクリメントしますNumOfAttempts
  • 同じ 5 分間のタイム スロット内でNumOfAttempts < 10 かどうかを確認する必要があります。FirstAttemptと の間で経過した分数を取得DateTime.Nowして mod 5 を実行できますが、これがどのタイムスロットにあるかはわかりません (つまり、ユーザーは 2 分の時点で 3 回試行してから戻ってくる可能性があります)。再試行を行うために 7 分で.modは私に同じ値を与えるでしょう.

どうすればこれを行うことができるかについて何か考えはありますか?

4

5 に答える 5

6

タイマーを使わないでください。このデータをユーザー データと一緒に永続化するためのスペースを確保し、何らかの形式の資格情報を保存します。最後のログイン試行時間と試行回数を保持するだけです。時間がnより大きい場合、次の試行で前のカウントがクリアされ、0 から開始されます。

于 2012-11-02T16:55:40.460 に答える
2

試行タイムスタンプのリストを保存し、それを使用してユーザーがロックアウトされているかどうかを判断できます。これは完全なコードではありませんが、基本的な考え方は次のとおりです。

// Store a list of attempts
var attempts = new List<DateTime>();

// Determine if 10 or more attempts have been made in the last 5 minutes
bool lockout = attempts.Where(a => (DateTime.Now - a).TotalMinutes <= 5).Count() >= 10;

// Register an attempt
attempts.Add(DateTime.Now);

// Remove attempts older than 5 minutes
attempts.Where(a => (DateTime.Now - a).TotalMinutes > 5).ToList()
    .ForEach(a => attempts.Remove(a));

試行をデータベースに保存することもできます。これにより、セキュリティ上の目的で紙の証跡が得られます. 同じ方法が適用されます。5 分未満のレコードをカウントします。

于 2012-11-02T17:02:35.620 に答える
1

答えは、要件をどのように解釈するかによって異なります。つまり、5 分間のタイム フレームはスライディング スケールですか?

要件を厳密に適用するには、ログイン試行用のデータベース テーブルを作成する必要があります。各レコードには、ユーザー レコードのキーとログイン試行のタイムスタンプが含まれます。次に、試行ごとに次のようなクエリを実行します

delete from login_attempts where user=[user] and attempt_time<[now - 5 minutes]
select count(*) from login_attempts where user=[user]

(準備済みステートメントを使用するかどうかに関係なく、括弧で囲まれた値は適切な値に置き換えられます)

次に、返されたカウントが 10 を超える場合、試行をブロックします。

(テーブルをきれいに保つために、毎回古い試行を削除することをお勧めします。もちろん、別の方法も可能です。)

要件を厳密に適用する必要がなく、新しいテーブルを作成したくない場合は、たとえば、最後の試行時間とカウントをユーザー レコードに追加することで、テーブルを簡素化できます。次に、試行ごとに(疑似コードはこちら...):

read last_attempt and attempt_count
if last_attempt < now - 5 minutes then
  attempt_count=0
endif
if attempt_count>10 then
  display error
else if attempt_fails then
  last_attempt=now
  attempt_count=attempt_count+1
endif

これは、規定された要件を厳密には満たしていません。ユーザーが 4 分ごとに 1 回試行した場合、40 分後には 10 回の不正な試行があったと言えますが、実際には 1 つを除いてすべて 5 分以上前のものでした。しかし、新しいテーブルを作成するよりも簡単で、私が推測する目標を達成し、誰かが力ずくでパスワードを解読するのを防ぎます.

于 2012-11-02T17:16:51.040 に答える
0

スライド式の 5 分間ウィンドウを実装するには、最初または最後の試行だけでなく、各試行の時間を知る必要があります。

ログイン試行ごとにデータベースに保存します。コメントで述べたように、セッション キャッシュを使用するだけでは簡単に回避できます。

username     | time
mr.mindor    | 00:00:00
mr.mindor    | 00:01:00

など、ログイン試行ごとに、試行回数が 10 回以下であることを確認します...

Select count(*) 
from LOGIN_ATTEMPTS 
  where username = @current_user 
  AND time > DATEADD(m,-5,GetDate())

セキュリティ/監査の目的で LOGIN_ATTEMPTS を無期限に保持することも、定期的にクリアすることも、ログインが成功した場合にクリアすることもできます。

最初または最後の時間とカウントのみを保存する際の問題。
毎回がなければ、試行の分布を判断できません。

最初の試行ベースのソリューション:

最初の試行時間と回数だけで、最初に続く 5 分間で 10 回を超える試行を防ぐことができますが、任意の 5 分間で 10 回を超える試行を防ぐことはできません。
例: 0:00 に 1 個、4:59 に 9 個、5:01 に 10 個、数秒で 19 個追加できます。

attempt_time | record
0:00         | {0:00, 1}
4:59         | {0:00, 2}
4:59         | {0:00, 3}
4:59         | {0:00, 4}
4:59         | {0:00, 5}
4:59         | {0:00, 6}
4:59         | {0:00, 7}
4:59         | {0:00, 8}
4:59         | {0:00, 9}
4:59         | {0:00, 10}  
5:01         | {5:00, 1}  // 10 since 4:59
5:02         | {5:00, 2}  // 11 since 4:59

最後の試行ベースのソリューション:
最後の試行時間と回数だけを保存すると、任意の 5 分間に 10 回を超える試行を防ぐことができますが、実際にははるかに制限的です。前回の試行から5分以内に10回以上の試行を防ぐようになります。
例: 4 分ごとに試行がある場合、カウンターはリセットされません。

attempt_time | record
0:00         | {0:00, 1}
4:00         | {4:00, 2}  // 2 in last 5 minutes
8:00         | {8:00, 3}  // 2 in last 5 minutes
...
40:00        | {40:00,11} // blocked but only 2 in last 5 minutes.
于 2012-11-02T17:12:29.233 に答える
0

タイマーはここでは醜いです。ある種の Attempts テーブルを保持するだけです。UserId、試行時間、および成功/失敗フラグを保存します。

そこから、ロジックは自明です。

于 2012-11-02T17:13:30.033 に答える