public void SubtractInPlace() { DecayingDouble d = new DecayingDouble(4, FirstDayOfCentury); d.SubtractInPlace(OneDay, 1, OneDayLater); Assert.InRange(d.ValueAtTimeOfLastUpdate, .99999, 1.000001); }
//public DecayingDouble AllFailures(TimeSpan halfLife) => AccountFailures.Add(halfLife, PasswordFailures); //public DecayingDouble AccountFailuresSubsetWithInfrequentPassword(TimeSpan halfLife) => AccountFailures.Subtract(halfLife, AccountFailuresSubsetWithFrequentPassword); //public DecayingDouble PasswordFailuresSubsetWithInfrequentPassword(TimeSpan halfLife) => PasswordFailures.Subtract(halfLife, PasswordFailuresSubsetWithFrequentPassword); //public DecayingDouble PasswordFailuresSubsetWithoutTypo(TimeSpan halfLife) => PasswordFailures.Subtract(halfLife, PasswordFailuresSubsetWithTypo); //public DecayingDouble PasswordFailuresSubsetWithoutEitherFrequentPasswordOrTypo(TimeSpan halfLife) => PasswordFailures.Subtract(halfLife, PasswordFailuresSubsetWithTypoAndFrequentPassword); /// <summary> /// This analysis will examine the client IP's previous failed attempts to login to this account /// to determine if any failed attempts were due to typos. /// </summary> /// <param name="account">The account that the client is currently trying to login to.</param> /// <param name="whenUtc"></param> /// <param name="correctPassword">The correct password for this account. (We can only know it because /// the client must have provided the correct one this loginAttempt.)</param> /// <returns></returns> public void AdjustBlockingScoreForPastTyposTreatedAsFullFailures( Simulator simulator, SimulatedUserAccount account, DateTime whenUtc, string correctPassword) { SimLoginAttemptSummaryForTypoAnalysis[] recentPotentialTypos = RecentPotentialTypos.MostRecentFirst.ToArray(); foreach (SimLoginAttemptSummaryForTypoAnalysis potentialTypo in recentPotentialTypos) { if (account == null || potentialTypo.UsernameOrAccountId != account.UsernameOrAccountId) { continue; } // Use an edit distance calculation to determine if it was a likely typo bool likelyTypo = EditDistance.Calculate(potentialTypo.Password, correctPassword) <= simulator._experimentalConfiguration.BlockingOptions.MaxEditDistanceConsideredATypo; TimeSpan halfLife = simulator._experimentalConfiguration.BlockingOptions.BlockScoreHalfLife; DecayingDouble value = new DecayingDouble(1d, potentialTypo.WhenUtc); // Add this to the list of changed attempts if (potentialTypo.WasPasswordFrequent) { PasswordFailuresNoTypoFrequentPassword.SubtractInPlace(halfLife, value); PasswordFailuresTypoFrequentPassword.AddInPlace(halfLife, value); } RecentPotentialTypos.Remove(potentialTypo); } }
public void SubtractInPlace() { DecayingDouble d = new DecayingDouble(4, FirstDayOfCentury); // One day later the double should be 2, so subtracting 1 should yield 1 d.SubtractInPlace(OneDay, 1, OneDayLater); Assert.InRange(d.ValueAtTimeOfLastUpdate, .99999, 1.000001); }
public IpHistory( IPAddress address, BlockingAlgorithmOptions options) { Address = address; CurrentBlockScore = new DecayingDouble(); RecentPotentialTypos = new SmallCapacityConstrainedSet <LoginAttemptSummaryForTypoAnalysis>(options.NumberOfFailuresToTrackForGoingBackInTimeToIdentifyTypos); }
public void AddInPlace() { DecayingDouble d = new DecayingDouble(4, FirstDayOfCentury); d.AddInPlace(OneDay, 3, TwoDaysLater); Assert.InRange(d.ValueAtTimeOfLastUpdate, 3.99999, 4.000001); double shouldBeVeryCloseTo1 = d.GetValue(OneDay, FourDaysLater); Assert.InRange(shouldBeVeryCloseTo1, .99999, 1.000001); }
public void UpdateSimulatorState(Simulator simulator, SimIpHistory ipHistory) { IsRepeatFailure = !IsPasswordValid && ( (SimAccount == null) ? simulator._recentIncorrectPasswords.AddMember(UserNameOrAccountId + "\n" + Password) : simulator._userAccountController.AddIncorrectPhaseTwoHash(SimAccount, Password, TimeOfAttemptUtc) ); int passwordsHeightOnBinomialLadder = (IsPasswordValid || IsRepeatFailure) ? simulator._binomialLadderFilter.GetHeight(Password) : simulator._binomialLadderFilter.Step(Password); IsFrequentlyGuessedPassword = passwordsHeightOnBinomialLadder >= simulator._experimentalConfiguration.BlockingOptions.BinomialLadderFrequencyThreshdold_T; DeviceCookieHadPriorSuccessfulLoginForThisAccount = SimAccount != null && simulator._userAccountController.HasClientWithThisHashedCookieSuccessfullyLoggedInBefore(SimAccount, CookieProvidedByBrowser); if (SimAccount != null && IsPasswordValid) { ipHistory.AdjustBlockingScoreForPastTyposTreatedAsFullFailures(simulator, SimAccount, TimeOfAttemptUtc, Password); simulator._userAccountController.RecordHashOfDeviceCookieUsedDuringSuccessfulLoginBackground( SimAccount, CookieProvidedByBrowser, TimeOfAttemptUtc); SimAccount.ConsecutiveIncorrectAttempts.SetValue(0, this.TimeOfAttemptUtc); } else if (SimAccount != null && !IsRepeatFailure) { SimAccount.ConsecutiveIncorrectAttempts.AddInPlace( simulator._experimentalConfiguration.BlockingOptions.BlockScoreHalfLife, 1d, this.TimeOfAttemptUtc); if (SimAccount.ConsecutiveIncorrectAttempts.GetValue( simulator._experimentalConfiguration.BlockingOptions.BlockScoreHalfLife) > SimAccount.MaxConsecutiveIncorrectAttempts.GetValue( simulator._experimentalConfiguration.BlockingOptions.BlockScoreHalfLife)) { SimAccount.MaxConsecutiveIncorrectAttempts.SetValue(SimAccount.ConsecutiveIncorrectAttempts); } } if (!IsPasswordValid && !IsRepeatFailure && SimAccount != null) { ipHistory.RecentPotentialTypos.Add(new SimLoginAttemptSummaryForTypoAnalysis() { WhenUtc = TimeOfAttemptUtc, Password = Password, UsernameOrAccountId = UserNameOrAccountId, WasPasswordFrequent = IsFrequentlyGuessedPassword }); } DecayingDouble decayingOneFromThisInstant = new DecayingDouble(1, TimeOfAttemptUtc); TimeSpan halfLife = simulator._experimentalConfiguration.BlockingOptions.BlockScoreHalfLife; if (IsPasswordValid) { ipHistory.SuccessfulLogins.AddInPlace(halfLife, decayingOneFromThisInstant); } else if (SimAccount == null) { if (IsRepeatFailure) { if (IsFrequentlyGuessedPassword) { ipHistory.RepeatAccountFailuresFrequentPassword.AddInPlace(halfLife, decayingOneFromThisInstant); } else { ipHistory.RepeatAccountFailuresInfrequentPassword.AddInPlace(halfLife, decayingOneFromThisInstant); } } else { if (IsFrequentlyGuessedPassword) { ipHistory.AccountFailuresFrequentPassword.AddInPlace(halfLife, decayingOneFromThisInstant); } else { ipHistory.AccountFailuresInfrequentPassword.AddInPlace(halfLife, decayingOneFromThisInstant); } } } else { if (IsRepeatFailure) { if (IsFrequentlyGuessedPassword) { ipHistory.RepeatPasswordFailuresNoTypoFrequentPassword.AddInPlace(halfLife, decayingOneFromThisInstant); } else { ipHistory.RepeatPasswordFailuresNoTypoInfrequentPassword.AddInPlace(halfLife, decayingOneFromThisInstant); } } else { if (IsFrequentlyGuessedPassword) { ipHistory.PasswordFailuresNoTypoFrequentPassword.AddInPlace(halfLife, decayingOneFromThisInstant); } else { ipHistory.PasswordFailuresNoTypoInfrequentPassword.AddInPlace(halfLife, decayingOneFromThisInstant); } } } }
public void UpdateSimulatorState(Simulator simulator, SimIpHistory ipHistory) { IsRepeatFailure = (SimAccount == null) ? simulator._recentIncorrectPasswords.AddMember(UserNameOrAccountId + "\n" + Password) : simulator._userAccountController.AddIncorrectPhaseTwoHashAsync(SimAccount, Password, TimeOfAttemptUtc).Result; int passwordsHeightOnBinomialLadder = IsPasswordValid ? simulator._binomialLadderFilter.GetHeight(Password) : simulator._binomialLadderFilter.Step(Password); IsFrequentlyGuessedPassword = passwordsHeightOnBinomialLadder + 1 >= simulator._binomialLadderFilter.MaxHeight; if (SimAccount != null) { DeviceCookieHadPriorSuccessfulLoginForThisAccount = simulator._userAccountController.HasClientWithThisHashedCookieSuccessfullyLoggedInBeforeAsync( SimAccount, CookieProvidedByBrowser).Result; } if (IsPasswordValid) { // Determine if any of the outcomes for login attempts from the client IP for this request were the result of typos, // as this might impact our decision about whether or not to block this client IP in response to its past behaviors. ipHistory.AdjustBlockingScoreForPastTyposTreatedAsFullFailures(simulator, SimAccount, TimeOfAttemptUtc, Password); if (SimAccount != null) { simulator._userAccountController.RecordHashOfDeviceCookieUsedDuringSuccessfulLoginBackground( SimAccount, CookieProvidedByBrowser, TimeOfAttemptUtc); } } if (!IsPasswordValid && !IsRepeatFailure && SimAccount != null) { ipHistory.RecentPotentialTypos.Add(new SimLoginAttemptSummaryForTypoAnalysis() { WhenUtc = TimeOfAttemptUtc, Password = Password, UsernameOrAccountId = UserNameOrAccountId, WasPasswordFrequent = IsFrequentlyGuessedPassword }); } DecayingDouble decayingOneFromThisInstant = new DecayingDouble(1, TimeOfAttemptUtc); TimeSpan halfLife = simulator._experimentalConfiguration.BlockingOptions.BlockScoreHalfLife; if (IsPasswordValid) { ipHistory.SuccessfulLogins.AddInPlace(halfLife, decayingOneFromThisInstant); } else if (SimAccount == null) { if (IsRepeatFailure) { if (IsFrequentlyGuessedPassword) { ipHistory.RepeatAccountFailuresFrequentPassword.AddInPlace(halfLife, decayingOneFromThisInstant); } else { ipHistory.RepeatAccountFailuresInfrequentPassword.AddInPlace(halfLife, decayingOneFromThisInstant); } } else { if (IsFrequentlyGuessedPassword) { ipHistory.AccountFailuresFrequentPassword.AddInPlace(halfLife, decayingOneFromThisInstant); } else { ipHistory.AccountFailuresInfrequentPassword.AddInPlace(halfLife, decayingOneFromThisInstant); } } } else { if (IsRepeatFailure) { if (IsFrequentlyGuessedPassword) { ipHistory.RepeatPasswordFailuresNoTypoFrequentPassword.AddInPlace(halfLife, decayingOneFromThisInstant); } else { ipHistory.RepeatPasswordFailuresNoTypoInfrequentPassword.AddInPlace(halfLife, decayingOneFromThisInstant); } } else { if (IsFrequentlyGuessedPassword) { ipHistory.PasswordFailuresNoTypoFrequentPassword.AddInPlace(halfLife, decayingOneFromThisInstant); } else { ipHistory.PasswordFailuresNoTypoInfrequentPassword.AddInPlace(halfLife, decayingOneFromThisInstant); } } } }
public override async Task <double> TryGetCreditAsync( DbUserAccount userAccount, double amountRequested, DateTime?timeOfRequestUtc = null, CancellationToken cancellationToken = default(CancellationToken)) { if (Double.IsNaN(amountRequested) || amountRequested <= 0) { // You can't request a negative amount and requesting nothing is free return(0); } DateTime timeOfRequestOrNowUtc = timeOfRequestUtc ?? DateTime.Now; double creditRetrieved = 0; using (var context = new DbUserAccountContext(DbOptions)) { using (var dbContextTransaction = await context.Database.BeginTransactionAsync(cancellationToken)) { bool rolledBackDueToConcurrencyException = false; do { try { DbUserAccountCreditBalance balance = await context.DbUserAccountCreditBalances.Where(b => b.DbUserAccountId == userAccount.DbUserAccountId) .FirstOrDefaultAsync(cancellationToken: cancellationToken); if (balance == null) { creditRetrieved = Math.Min(amountRequested, userAccount.CreditLimit); double amountRemaining = userAccount.CreditLimit - creditRetrieved; context.DbUserAccountCreditBalances.Add( new DbUserAccountCreditBalance() { DbUserAccountId = userAccount.DbUserAccountId, ConsumedCreditsLastUpdatedUtc = timeOfRequestOrNowUtc, ConsumedCreditsLastValue = amountRemaining }); } else { double amountAvailable = Math.Max(0, userAccount.CreditLimit - DecayingDouble.Decay(balance.ConsumedCreditsLastValue, userAccount.CreditHalfLife, balance.ConsumedCreditsLastUpdatedUtc, timeOfRequestOrNowUtc)); if (amountAvailable > 0) { creditRetrieved = Math.Min(amountRequested, amountAvailable); } double amountRemaining = amountAvailable - creditRetrieved; balance.ConsumedCreditsLastValue = amountRemaining; balance.ConsumedCreditsLastUpdatedUtc = timeOfRequestOrNowUtc; } await context.SaveChangesAsync(cancellationToken); dbContextTransaction.Commit(); } catch (DbUpdateConcurrencyException) { dbContextTransaction.Rollback(); rolledBackDueToConcurrencyException = true; } } while (rolledBackDueToConcurrencyException); } } return(creditRetrieved); }
public void DecayOverTwoHalfLives() { double shouldBeVeryCloseTo1 = DecayingDouble.Decay(4, OneDay, FirstDayOfCentury, TwoDaysLater); Assert.InRange(shouldBeVeryCloseTo1, .99999, 1.000001); }
public void UpdateSimulatorState(Simulator simulator, SimIpHistory ipHistory) { IsRepeatFailure = !IsPasswordValid && ( (SimAccount == null) ? simulator._recentIncorrectPasswords.AddMember(UserNameOrAccountId + "\n" + Password) : simulator._userAccountController.AddIncorrectPhaseTwoHash(SimAccount, Password, TimeOfAttemptUtc) ); int passwordsHeightOnBinomialLadder = (IsPasswordValid || IsRepeatFailure) ? simulator._binomialLadderFilter.GetHeight(Password) : simulator._binomialLadderFilter.Step(Password); IsFrequentlyGuessedPassword = passwordsHeightOnBinomialLadder >= simulator._experimentalConfiguration.BlockingOptions.BinomialLadderFrequencyThreshdold_T; DeviceCookieHadPriorSuccessfulLoginForThisAccount = SimAccount != null && simulator._userAccountController.HasClientWithThisHashedCookieSuccessfullyLoggedInBefore(SimAccount, CookieProvidedByBrowser); if (SimAccount != null && IsPasswordValid) { // Determine if any of the outcomes for login attempts from the client IP for this request were the result of typos, // as this might impact our decision about whether or not to block this client IP in response to its past behaviors. ipHistory.AdjustBlockingScoreForPastTyposTreatedAsFullFailures(simulator, SimAccount, TimeOfAttemptUtc, Password); simulator._userAccountController.RecordHashOfDeviceCookieUsedDuringSuccessfulLoginBackground( SimAccount, CookieProvidedByBrowser, TimeOfAttemptUtc); // Clear the count of consecutive failures SimAccount.ConsecutiveIncorrectAttempts.SetValue(0, this.TimeOfAttemptUtc); } else if (SimAccount != null && !IsRepeatFailure) { // Add the the account's consecutive failure count SimAccount.ConsecutiveIncorrectAttempts.AddInPlace( simulator._experimentalConfiguration.BlockingOptions.BlockScoreHalfLife, 1d, this.TimeOfAttemptUtc); // Increase the max consecutive faiulre count if the current consecutive failure count exceeds it if (SimAccount.ConsecutiveIncorrectAttempts.GetValue( simulator._experimentalConfiguration.BlockingOptions.BlockScoreHalfLife) > SimAccount.MaxConsecutiveIncorrectAttempts.GetValue( simulator._experimentalConfiguration.BlockingOptions.BlockScoreHalfLife)) { SimAccount.MaxConsecutiveIncorrectAttempts.SetValue(SimAccount.ConsecutiveIncorrectAttempts); } } if (!IsPasswordValid && !IsRepeatFailure && SimAccount != null) { // This attempt is a non-repeat failure and could be a typo. Store it in the ste of potential typos. ipHistory.RecentPotentialTypos.Add(new SimLoginAttemptSummaryForTypoAnalysis() { WhenUtc = TimeOfAttemptUtc, Password = Password, UsernameOrAccountId = UserNameOrAccountId, WasPasswordFrequent = IsFrequentlyGuessedPassword }); } DecayingDouble decayingOneFromThisInstant = new DecayingDouble(1, TimeOfAttemptUtc); TimeSpan halfLife = simulator._experimentalConfiguration.BlockingOptions.BlockScoreHalfLife; if (IsPasswordValid) { ipHistory.SuccessfulLogins.AddInPlace(halfLife, decayingOneFromThisInstant); } else if (SimAccount == null) { if (IsRepeatFailure) { if (IsFrequentlyGuessedPassword) { ipHistory.RepeatAccountFailuresFrequentPassword.AddInPlace(halfLife, decayingOneFromThisInstant); } else { ipHistory.RepeatAccountFailuresInfrequentPassword.AddInPlace(halfLife, decayingOneFromThisInstant); } } else { if (IsFrequentlyGuessedPassword) { ipHistory.AccountFailuresFrequentPassword.AddInPlace(halfLife, decayingOneFromThisInstant); } else { ipHistory.AccountFailuresInfrequentPassword.AddInPlace(halfLife, decayingOneFromThisInstant); } } } else { if (IsRepeatFailure) { if (IsFrequentlyGuessedPassword) { ipHistory.RepeatPasswordFailuresNoTypoFrequentPassword.AddInPlace(halfLife, decayingOneFromThisInstant); } else { ipHistory.RepeatPasswordFailuresNoTypoInfrequentPassword.AddInPlace(halfLife, decayingOneFromThisInstant); } } else { if (IsFrequentlyGuessedPassword) { ipHistory.PasswordFailuresNoTypoFrequentPassword.AddInPlace(halfLife, decayingOneFromThisInstant); } else { ipHistory.PasswordFailuresNoTypoInfrequentPassword.AddInPlace(halfLife, decayingOneFromThisInstant); } } } }