Security Patches (#1624)

* Fixed an issue where migrate-email could be called when an admin already existed

* When logging in, ensure there is no bias towards username or password when rejecting

* Cleaned up some messaging around anonymous apis to ensure there are no attack vectors.
This commit is contained in:
Joe Milazzo 2022-10-31 09:07:55 -04:00 committed by GitHub
parent cac8577925
commit f8db37d3f9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

View File

@ -186,7 +186,7 @@ public class AccountController : BaseApiController
.Include(u => u.UserPreferences) .Include(u => u.UserPreferences)
.SingleOrDefaultAsync(x => x.NormalizedUserName == loginDto.Username.ToUpper()); .SingleOrDefaultAsync(x => x.NormalizedUserName == loginDto.Username.ToUpper());
if (user == null) return Unauthorized("Invalid username"); if (user == null) return Unauthorized("Your credentials are not correct");
var result = await _signInManager var result = await _signInManager
.CheckPasswordSignInAsync(user, loginDto.Password, true); .CheckPasswordSignInAsync(user, loginDto.Password, true);
@ -198,7 +198,7 @@ public class AccountController : BaseApiController
if (!result.Succeeded) if (!result.Succeeded)
{ {
return Unauthorized(result.IsNotAllowed ? "You must confirm your email first" : "Your credentials are not correct."); return Unauthorized(result.IsNotAllowed ? "You must confirm your email first" : "Your credentials are not correct");
} }
// Update LastActive on account // Update LastActive on account
@ -632,6 +632,11 @@ public class AccountController : BaseApiController
return BadRequest("There was an error setting up your account. Please check the logs"); return BadRequest("There was an error setting up your account. Please check the logs");
} }
/// <summary>
/// Last step in authentication flow, confirms the email token for email
/// </summary>
/// <param name="dto"></param>
/// <returns></returns>
[AllowAnonymous] [AllowAnonymous]
[HttpPost("confirm-email")] [HttpPost("confirm-email")]
public async Task<ActionResult<UserDto>> ConfirmEmail(ConfirmEmailDto dto) public async Task<ActionResult<UserDto>> ConfirmEmail(ConfirmEmailDto dto)
@ -640,7 +645,8 @@ public class AccountController : BaseApiController
if (user == null) if (user == null)
{ {
return BadRequest("The email does not match the registered email"); _logger.LogInformation("confirm-email failed from invalid registered email: {Email}", dto.Email);
return BadRequest("Invalid email confirmation");
} }
// Validate Password and Username // Validate Password and Username
@ -654,7 +660,11 @@ public class AccountController : BaseApiController
} }
if (!await ConfirmEmailToken(dto.Token, user)) return BadRequest("Invalid Email Token"); if (!await ConfirmEmailToken(dto.Token, user))
{
_logger.LogInformation("confirm-email failed from invalid token: {Token}", dto.Token);
return BadRequest("Invalid email confirmation");
}
user.UserName = dto.Username; user.UserName = dto.Username;
user.ConfirmationToken = null; user.ConfirmationToken = null;
@ -694,11 +704,15 @@ public class AccountController : BaseApiController
var user = await _unitOfWork.UserRepository.GetUserByConfirmationToken(dto.Token); var user = await _unitOfWork.UserRepository.GetUserByConfirmationToken(dto.Token);
if (user == null) if (user == null)
{ {
return BadRequest("Invalid Email Token"); _logger.LogInformation("confirm-email failed from invalid registered email: {Email}", dto.Email);
return BadRequest("Invalid email confirmation");
} }
if (!await ConfirmEmailToken(dto.Token, user)) return BadRequest("Invalid Email Token"); if (!await ConfirmEmailToken(dto.Token, user))
{
_logger.LogInformation("confirm-email failed from invalid token: {Token}", dto.Token);
return BadRequest("Invalid email confirmation");
}
_logger.LogInformation("User is updating email from {OldEmail} to {NewEmail}", user.Email, dto.Email); _logger.LogInformation("User is updating email from {OldEmail} to {NewEmail}", user.Email, dto.Email);
var result = await _userManager.SetEmailAsync(user, dto.Email); var result = await _userManager.SetEmailAsync(user, dto.Email);
@ -728,12 +742,16 @@ public class AccountController : BaseApiController
var user = await _unitOfWork.UserRepository.GetUserByEmailAsync(dto.Email); var user = await _unitOfWork.UserRepository.GetUserByEmailAsync(dto.Email);
if (user == null) if (user == null)
{ {
return BadRequest("Invalid Details"); return BadRequest("Invalid credentials");
} }
var result = await _userManager.VerifyUserTokenAsync(user, TokenOptions.DefaultProvider, var result = await _userManager.VerifyUserTokenAsync(user, TokenOptions.DefaultProvider,
"ResetPassword", dto.Token); "ResetPassword", dto.Token);
if (!result) return BadRequest("Unable to reset password, your email token is not correct."); if (!result)
{
_logger.LogInformation("Unable to reset password, your email token is not correct: {@Dto}", dto);
return BadRequest("Invalid credentials");
}
var errors = await _accountService.ChangeUserPassword(user, dto.Password); var errors = await _accountService.ChangeUserPassword(user, dto.Password);
return errors.Any() ? BadRequest(errors) : Ok("Password updated"); return errors.Any() ? BadRequest(errors) : Ok("Password updated");
@ -801,9 +819,13 @@ public class AccountController : BaseApiController
public async Task<ActionResult<UserDto>> ConfirmMigrationEmail(ConfirmMigrationEmailDto dto) public async Task<ActionResult<UserDto>> ConfirmMigrationEmail(ConfirmMigrationEmailDto dto)
{ {
var user = await _unitOfWork.UserRepository.GetUserByEmailAsync(dto.Email); var user = await _unitOfWork.UserRepository.GetUserByEmailAsync(dto.Email);
if (user == null) return BadRequest("This email is not on system"); if (user == null) return BadRequest("Invalid credentials");
if (!await ConfirmEmailToken(dto.Token, user)) return BadRequest("Invalid Email Token"); if (!await ConfirmEmailToken(dto.Token, user))
{
_logger.LogInformation("confirm-migration-email email token is invalid");
return BadRequest("Invalid credentials");
}
await _unitOfWork.CommitAsync(); await _unitOfWork.CommitAsync();
@ -865,6 +887,10 @@ public class AccountController : BaseApiController
[HttpPost("migrate-email")] [HttpPost("migrate-email")]
public async Task<ActionResult<string>> MigrateEmail(MigrateUserEmailDto dto) public async Task<ActionResult<string>> MigrateEmail(MigrateUserEmailDto dto)
{ {
// If there is an admin account already, return
var users = await _unitOfWork.UserRepository.GetAdminUsersAsync();
if (users.Any()) return BadRequest("Admin already exists");
// Check if there is an existing invite // Check if there is an existing invite
var emailValidationErrors = await _accountService.ValidateEmail(dto.Email); var emailValidationErrors = await _accountService.ValidateEmail(dto.Email);
if (emailValidationErrors.Any()) if (emailValidationErrors.Any())