/// <summary> /// Parses the return data from the HIBP API into a list of PasswordHashEntry object for simplification purposes. /// </summary> /// <param name="passwordsRange">One long string containing multiple newline-seperated entries. The entries are semicolom seperated hash:occurences object, e.g. 00D4F6E8FA6EECAD2A3AA415EEC418D38EC:2</param> /// <returns>A list of parsed PasswordHashEntry objects, based on HIBP API return data.</returns> private List <PasswordHashEntry> ParsePasswordsRangeFromApi(string passwordsRange) { List <PasswordHashEntry> entries = null; if (!string.IsNullOrEmpty(passwordsRange)) { entries = new List <PasswordHashEntry>(); } // Split entries on NewLine character (\r\n). // PasswordsRange contains for example: 00D4F6E8FA6EECAD2A3AA415EEC418D38EC:2\r\n011053FD0102E94D6AE2F8B83D76FAF94F6:1 foreach (var hashAndOccurences in passwordsRange.Split(new[] { Environment.NewLine }, StringSplitOptions.None)) { string[] items = hashAndOccurences.Split(':'); PasswordHashEntry passwordHashEntry = new PasswordHashEntry() { Hash = items[0], Occurences = int.Parse(items[1]) }; entries.Add(passwordHashEntry); } return(entries); }
/// <summary> /// Gets the amount of occurences based on hashed password and store it in a PasswordHashEntry. /// </summary> /// <param name="hashedPassword">SHA-1 hashed password.</param> /// <returns>Filled PasswordHashEntry with total amount of occurences.</returns> public async Task <PasswordHashEntry> GetPasswordOccurences(string hashedPassword) { PasswordHashEntry passwordHashEntry = null; try { string uriString = $"{baseUrl}/pwnedpassword/{hashedPassword}"; Uri uri = new Uri(uriString); string content = await GetJsonFromUri(uri); if (int.TryParse(content, out int occurences)) { passwordHashEntry = new PasswordHashEntry() { Hash = hashedPassword, Occurences = occurences }; } } catch (Exception e) { System.Diagnostics.Debug.WriteLine($"HttpUtils: GetPasswordOccurences: Cannot get password occurences. Message: {e}"); } return(passwordHashEntry); }
private async Task <bool> PasswordIsSafe(string password) { bool passwordIsSafe = false; string sha1HashedPassword = HashingHelper.Hash(password); var hashedPasswordEntries = await _httpUtils.GetPasswordsByRange(sha1HashedPassword); int similarOccurences = 0; PasswordHashEntry foundEntry = hashedPasswordEntries.Find(entry => entry.Hash.Equals(sha1HashedPassword)); foreach (var passwordHashEntry in hashedPasswordEntries) { similarOccurences += passwordHashEntry.Occurences; } _logger.LogInformation($"Found {similarOccurences} hashes."); // The password is safe if it does not occur in the found hashes. if (foundEntry == null) { _logger.LogInformation($"No exact Hash match found."); passwordIsSafe = true; } else { _logger.LogInformation($"Found: {foundEntry.Hash} with {foundEntry.Occurences} occurences"); } return(passwordIsSafe); }
private void AddPasswordError(PasswordHashEntry passwordHashEntry) { var url = Url.Action("PasswordHelp", "Account"); string passwordError = $"The chosen password is unsafe and has previously appeared {passwordHashEntry.Occurences} times in password breaches. Please choose a different one."; ModelState.AddModelError("passwordError", passwordError); }
private async Task <PasswordHashEntry> GetPasswordHashEntryAsync(string password) { string sha1HashedPassword = HashingHelper.Hash(password); PasswordHashEntry passwordHashEntry = await _httpUtils.GetPasswordOccurences(sha1HashedPassword); if (passwordHashEntry != null) { _logger.LogInformation($"Found: {passwordHashEntry.Hash} with {passwordHashEntry.Occurences} occurences"); } else if (passwordHashEntry == null) { _logger.LogInformation($"No exact Hash match found."); } return(passwordHashEntry); }
public async Task <IActionResult> Register(RegisterViewModel model, string returnUrl = null) { ViewData["ReturnUrl"] = returnUrl; if (ModelState.IsValid) { var user = new ApplicationUser { UserName = model.Email, Email = model.Email }; PasswordHashEntry passwordHashEntry = await GetPasswordHashEntryAsync(model.Password); if (passwordHashEntry == null) { var result = await _userManager.CreateAsync(user, model.Password); if (result.Succeeded) { _logger.LogInformation("User created a new account with password."); var code = await _userManager.GenerateEmailConfirmationTokenAsync(user); var callbackUrl = Url.EmailConfirmationLink(user.Id, code, Request.Scheme); await _emailSender.SendEmailConfirmationAsync(model.Email, callbackUrl); await _signInManager.SignInAsync(user, isPersistent : false); _logger.LogInformation("User created a new account with password."); return(RedirectToLocal(returnUrl)); } AddErrors(result); } else { AddPasswordError(passwordHashEntry); } } return(View(model)); }