From e198838a32490efeca2ba5bd1188354261e695b5 Mon Sep 17 00:00:00 2001 From: Garrritt McCune Date: Wed, 13 Jan 2021 11:24:19 -0600 Subject: [PATCH] Cleaned up and refactored the code to make things much easier to understand. --- .vs/SecureCore/v16/.suo | Bin 67584 -> 58880 bytes SecureCore/AppSettingsManager.cs | 6 +- SecureCore/Authentication/PasswordManager.cs | 62 +++--- SecureCore/Authentication/SessionManager.cs | 68 +++++-- SecureCore/Controllers/AuthController.cs | 81 ++++---- SecureCore/Services/UserDataService.cs | 199 ++----------------- 6 files changed, 149 insertions(+), 267 deletions(-) diff --git a/.vs/SecureCore/v16/.suo b/.vs/SecureCore/v16/.suo index f2d8eb63d6f5a26505e6c317866ed8aa5de02a4e..21a296fbbf84dfa512830f25040ea3abd6d517b7 100644 GIT binary patch delta 4988 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 delta 6227 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 MaxPasswordLength) return (false, $"Your password is too long, it must not exceed {MaxPasswordLength} characters and must contain at least {MinPasswordLength} characters."); @@ -45,35 +39,53 @@ namespace SecureCore.Authentication if (passwordHash == GetHash(password, saltBytes)) return (true, string.Empty); else - return (false, string.Empty); + return (false, "User name or password is not correct."); } - public static (string PasswordHash, string SaltHash) GetUserPasswordHash(int userId) + public static void InsertPasswordResetRequest(string userName, string sessionToken, DateTime expirationDate, string userAgent, string ipAddress, string connectionString) { - using (var connection = new SqlConnection(ConnectionString)) - { - using (var command = new SqlCommand("SELECT [Password Hash], [Salt Hash] FROM Login WHERE [User Key] = @UserId", connection)) - { - command.Parameters.AddWithValue("UserId", userId); + using var connection = new SqlConnection(connectionString); - connection.Open(); + using var command = new SqlCommand("LogPasswordResetRequest", connection) { CommandType = CommandType.StoredProcedure }; - var reader = command.ExecuteReader(); + command.Parameters.AddWithValue("SessionToken", sessionToken); + command.Parameters.AddWithValue("ExpirationDate", expirationDate); + command.Parameters.AddWithValue("UserName", userName); + command.Parameters.AddWithValue("UserAgent", userAgent); + command.Parameters.AddWithValue("IpAddress", ipAddress); - if (!reader.HasRows) throw new MissingFieldException("No login records exist for this user."); + connection.Open(); - reader.Read(); + command.ExecuteNonQuery(); + } - return (reader["Password Hash"].ToString(), reader["Salt Hash"].ToString()); - } - } + //TODO: Reset Password Function Here + + public static (string PasswordHash, string Salt) GetPasswordHashAndSalt(string username, string connectionString) + { + using var connection = new SqlConnection(connectionString); + + using var command = new SqlCommand("SELECT [Password Hash], [Salt] FROM dbo.GetUserPasswordHashAndSalt(@Username)", connection); + + command.Parameters.AddWithValue("Username", username); + + connection.Open(); + + var reader = command.ExecuteReader(); + + reader.Read(); + + if (!reader.HasRows) return (string.Empty, string.Empty); + + return (reader["Password Hash"].ToString(), reader["Salt"].ToString()); } public static string HashStringData(string data, string salt = "") { - var saltBytes = new byte[0]; + _ = new byte[0]; + byte[] saltBytes; - if(!string.IsNullOrEmpty(salt)) + if (!string.IsNullOrEmpty(salt)) saltBytes = Convert.FromBase64String(salt); else { diff --git a/SecureCore/Authentication/SessionManager.cs b/SecureCore/Authentication/SessionManager.cs index 53de0b2..81e1f65 100644 --- a/SecureCore/Authentication/SessionManager.cs +++ b/SecureCore/Authentication/SessionManager.cs @@ -1,25 +1,14 @@ using System; +using System.Data; using System.Data.SqlClient; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; namespace SecureCore.Authentication { - public class SessionManager + public static class SessionManager { - private int SessionKeySize { get; } = 64; //n bytes - private string ConnectionString { get; set; } + private static int SessionKeySize { get; } = 64; //n bytes - public SessionManager() - { - if (AppSettingsManager.TryGetConnectionStringByName("MainDataConnectionString", out string connection)) - ConnectionString = connection; - else - throw new Exception("Failed to get connection string."); - } - - public string CreateSessionToken() + public static string CreateSessionToken() { var token = new byte[SessionKeySize]; @@ -28,13 +17,50 @@ namespace SecureCore.Authentication return Convert.ToBase64String(token); } - //public string CreatePasswordRecoveryKey(string userName) - //{ + public static bool IsSessionTokenValid(string sessionToken, string connectionString, bool isResetToken = false) + { + using var connection = new SqlConnection(connectionString); - // using (var connection = new SqlConnection(ConnectionString)) - // { + using var command = new SqlCommand("ValidateSessionToken", connection) { CommandType = CommandType.StoredProcedure }; - // } - //} + command.Parameters.AddWithValue("SessionToken", sessionToken); + command.Parameters.AddWithValue("IsResetToken", isResetToken); + + connection.Open(); + + var result = command.ExecuteScalar(); + + return Convert.ToBoolean(result); + } + + public static void Logout(string sessionToken, string connectionString) + { + using var connection = new SqlConnection(connectionString); + + using var command = new SqlCommand("LogoutUser", connection) { CommandType = CommandType.StoredProcedure }; + + command.Parameters.AddWithValue("SessionToken", sessionToken); + + connection.Open(); + + command.ExecuteNonQuery(); + } + + public static void Login(string userName, string sessionToken, DateTime expirationDate, string userAgent, string ipAddress, string connectionString) + { + using var connection = new SqlConnection(connectionString); + + using var command = new SqlCommand("LoginUser", connection) { CommandType = CommandType.StoredProcedure }; + + command.Parameters.AddWithValue("SessionToken", sessionToken); + command.Parameters.AddWithValue("ExpirationDate", expirationDate); + command.Parameters.AddWithValue("UserName", userName); + command.Parameters.AddWithValue("UserAgent", userAgent); + command.Parameters.AddWithValue("IpAddress", ipAddress); + + connection.Open(); + + command.ExecuteNonQuery(); + } } } diff --git a/SecureCore/Controllers/AuthController.cs b/SecureCore/Controllers/AuthController.cs index 81b0a63..ac48bf4 100644 --- a/SecureCore/Controllers/AuthController.cs +++ b/SecureCore/Controllers/AuthController.cs @@ -3,7 +3,7 @@ using Microsoft.AspNetCore.Http; using System; using System.Web; using System.Collections.Generic; -using System.Linq; +using System.Data.SqlClient; using System.Threading.Tasks; using SecureCore.Services; using SecureCore.Authentication; @@ -19,39 +19,44 @@ namespace SecureCore.Controllers [AcceptVerbs("POST")] public IActionResult Login([FromBody] LoginInfo info) { - //NOTE: password length should be at most 64 - 128 characters long. - //Very the user has login data. - //if (!UserDataService.UserHasLoginData(info.UserName)) return Unauthorized("User doesn't have login creds"); - var result = UserDataService.UserNameIsValid(info.UserName); - - if (!result.IsValid) return Unauthorized(result.Message); - - //if (HttpContext.Request.Cookies.ContainsKey("Session")) - //{ - // if(UserDataService.IsSessionTokenValid(HttpContext.Request.Cookies["Session"]))//, info.UserName)) - // return Ok($"Session is live{Environment.NewLine}"); - //} - - var (password, salt) = UserDataService.GetUserPasswordHash(info.UserName); - var agent = HttpContext.Request.Headers[Microsoft.Net.Http.Headers.HeaderNames.UserAgent]; - var ip = PasswordManager.HashStringData(Request.HttpContext.Connection.RemoteIpAddress.MapToIPv4().ToString(), salt); - result = PasswordManager.PasswordIsValid(info.Password, salt, password); - - if (result.IsValid) + AppSettingsManager.TryGetConnectionStringByName("MainDataConnectionString", out string connectionString); + //TODO: see if the request has an active session key so we don't spam out new keys like mad + try { - var session = new SessionManager(); + //Verify that the username provided is valid, i.e. no whitespace, special characters, etc. + var result = UserDataService.IsUsernameValid(info.UserName); + //If the name isn't valid, return the error message to the user. + if (!result.IsValid) return Unauthorized(result.Message); - var sessionToken = session.CreateSessionToken(); + //if (HttpContext.Request.Cookies.ContainsKey("Session")) + //{ + // if(UserDataService.IsSessionTokenValid(HttpContext.Request.Cookies["Session"]))//, info.UserName)) + // return Ok($"Session is live{Environment.NewLine}"); + //} + //Next try to get the user's login data, if the fuction returns empty strings, then the user isn't a registered name. + var (password, salt) = PasswordManager.GetPasswordHashAndSalt(info.UserName, connectionString); + //If the user name isn't in the system, then simply return a generic error message about something not being right. + if (password == string.Empty) return Unauthorized("User name or password is not correct."); - UserDataService.SetUserSessionToken(info.UserName, sessionToken, DateTime.Now.AddDays(7), agent, ip); + result = PasswordManager.IsPasswordValid(info.Password, salt, password); - HttpContext.Response.Cookies.Append("Session", sessionToken, GetCookieOptions()); + if (!result.IsValid) return Unauthorized(result.Message); + + var sessionToken = SessionManager.CreateSessionToken(); + var agent = HttpContext.Request.Headers[Microsoft.Net.Http.Headers.HeaderNames.UserAgent]; + var ip = PasswordManager.HashStringData(Request.HttpContext.Connection.RemoteIpAddress.MapToIPv4().ToString(), salt); + var cookieOptions = GetCookieOptions(); + + SessionManager.Login(info.UserName, sessionToken, cookieOptions.Expires.Value.UtcDateTime, agent, ip, connectionString); + + HttpContext.Response.Cookies.Append("Session", sessionToken, cookieOptions); return Ok($"Logged in success{Environment.NewLine}"); } - else + catch(Exception ex) { - return Unauthorized(result.Message); + //TODO: Log this event. + return Unauthorized("An error has occured trying to process your request, please try again in a few minutes."); } } @@ -59,17 +64,19 @@ namespace SecureCore.Controllers [AcceptVerbs("POST")] public IActionResult Register([FromBody] LoginInfo info) { + AppSettingsManager.TryGetConnectionStringByName("MainDataConnectionString", out string connectionString); var (hash, salt) = PasswordManager.HashPassword(info.Password); - var session = new SessionManager(); - var sessionToken = session.CreateSessionToken(); + var sessionToken = SessionManager.CreateSessionToken(); var agent = HttpContext.Request.Headers[Microsoft.Net.Http.Headers.HeaderNames.UserAgent]; var ip = PasswordManager.HashStringData(Request.HttpContext.Connection.RemoteIpAddress.MapToIPv4().ToString(), salt); try { - var i = UserDataService.RegisterNewUser(info.UserName, info.Email, hash, salt, sessionToken, DateTime.Now.AddDays(7),agent, ip); + var cookieOptions = GetCookieOptions(); - HttpContext.Response.Cookies.Append("Session", sessionToken, GetCookieOptions()); + var i = UserDataService.RegisterNewUser(info.UserName, info.Email, hash, salt, sessionToken, cookieOptions.Expires.Value.UtcDateTime, agent, ip, connectionString); + + HttpContext.Response.Cookies.Append("Session", sessionToken, cookieOptions); return Ok($"New User ID: {i}"); } @@ -83,7 +90,9 @@ namespace SecureCore.Controllers [AcceptVerbs("POST")] public IActionResult Logout() { - UserDataService.DestroySession(HttpContext.Request.Cookies["Session"]); + AppSettingsManager.TryGetConnectionStringByName("MainDataConnectionString", out string connectionString); + + SessionManager.Logout(HttpContext.Request.Cookies["Session"], connectionString); HttpContext.Response.Cookies.Delete("Session"); @@ -94,9 +103,11 @@ namespace SecureCore.Controllers //[AcceptVerbs("GET")] public IActionResult ResetPassword([FromQuery] string token) { + AppSettingsManager.TryGetConnectionStringByName("MainDataConnectionString", out string connectionString); + try { - if (!UserDataService.IsResetTokenValid(token)) return Unauthorized("Token invalid"); + if (!SessionManager.IsSessionTokenValid(token, connectionString, true)) return Unauthorized("Token invalid"); } catch (Exception e) @@ -110,14 +121,14 @@ namespace SecureCore.Controllers [AcceptVerbs("POST")] public IActionResult CreatePasswordResetLink([FromBody] string userName) //TODO: this sig should only accept an email, so the link can be sent there. { - var sessionManager = new SessionManager(); - var token = sessionManager.CreateSessionToken(); + AppSettingsManager.TryGetConnectionStringByName("MainDataConnectionString", out string connectionString); + var token = SessionManager.CreateSessionToken(); var agent = HttpContext.Request.Headers[Microsoft.Net.Http.Headers.HeaderNames.UserAgent]; var ip = PasswordManager.HashStringData(Request.HttpContext.Connection.RemoteIpAddress.MapToIPv4().ToString()); try { - UserDataService.SetUserSessionToken(userName, token, DateTime.Now.AddHours(1), agent, ip, true); + PasswordManager.InsertPasswordResetRequest(userName, token, DateTime.Now.AddHours(1), agent, ip, connectionString); token = HttpUtility.UrlEncode(token); diff --git a/SecureCore/Services/UserDataService.cs b/SecureCore/Services/UserDataService.cs index 59dabb1..cb307ff 100644 --- a/SecureCore/Services/UserDataService.cs +++ b/SecureCore/Services/UserDataService.cs @@ -3,7 +3,6 @@ using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using System.Data; -using System.Transactions; using System.Data.SqlClient; namespace SecureCore.Services @@ -13,202 +12,40 @@ namespace SecureCore.Services // public static int UserNameMaxLength { get; } = 64; - private static string ConnectionString = @"Server=DESKTOP-OEDDVKC\SQLEXPRESS;Database=main;Integrated Security=true;"; - - public static (bool IsValid, string Message) UserNameIsValid(string userName) + public static (bool IsValid, string Message) IsUsernameValid(string userName) { - if (userName.Length > UserNameMaxLength) return (false, $"Username to long, must not exceed {UserNameMaxLength} characters."); + if (userName.Length > UserNameMaxLength) return (false, $"Username too long, must not exceed {UserNameMaxLength} characters."); var invalidChars = new List(); foreach(var c in userName) - { - if (char.IsPunctuation(c) || char.IsSymbol(c) || char.IsControl(c) || char.IsSeparator(c) || char.IsWhiteSpace(c)) invalidChars.Add(c);//return (false, $"The character '{c}' is not allowed."); - } + if (char.IsPunctuation(c) || char.IsSymbol(c) || char.IsControl(c) || char.IsSeparator(c) || char.IsWhiteSpace(c)) invalidChars.Add(c); - if (invalidChars.Count > 0) return (false, $"The characters '{string.Join(",", invalidChars)}' are not allowed in a user name."); + if (invalidChars.Count > 0) return (false, $"The character(s) '{string.Join(",", invalidChars)}' are not allowed in a user name."); return (true, string.Empty); } - public static (string PasswordHash, string SaltHash) GetUserPasswordHash(string userName) + public static int RegisterNewUser(string userName, string email, string passwordHash, string saltHash, string sessionToken, DateTime expirationDate, string userAgent, string ipAddress, string connectionString) { - var userId = GetUserId(userName); + using var connection = new SqlConnection(connectionString); - using(var connection = new SqlConnection(ConnectionString)) - { - using(var command = new SqlCommand("SELECT [Password Hash], [Salt] FROM Login WHERE [User Key] = @UserId", connection)) - { - command.Parameters.AddWithValue("UserId", userId); + using var command = new SqlCommand("RegisterNewUser", connection) { CommandType = CommandType.StoredProcedure }; - connection.Open(); + command.Parameters.AddWithValue("UserName", userName); + command.Parameters.AddWithValue("Email", email); + command.Parameters.AddWithValue("Password", passwordHash); + command.Parameters.AddWithValue("Salt", saltHash); + command.Parameters.AddWithValue("SessionToken", sessionToken); + command.Parameters.AddWithValue("ExpirationDate", expirationDate); + command.Parameters.AddWithValue("UserAgent", userAgent); + command.Parameters.AddWithValue("IpAddress", ipAddress); - var reader = command.ExecuteReader(); + connection.Open(); - reader.Read(); + var result = command.ExecuteScalar(); - return (reader["Password Hash"].ToString(), reader["Salt"].ToString()); - } - } + return Convert.ToInt32(result); } - - public static bool UserHasLoginData(string userName) - { - var userId = GetUserId(userName); - - using (var connection = new SqlConnection(ConnectionString)) - { - using (var command = new SqlCommand("SELECT [Password Hash], [Salt Hash] FROM Login WHERE [User Key] = @UserId", connection)) - { - command.Parameters.AddWithValue("UserId", userId); - - connection.Open(); - - var reader = command.ExecuteReader(); - - return reader.HasRows; - } - } - } - - public static int GetUserId(string userName) - { - using (var connection = new SqlConnection(ConnectionString)) - { - using (var command = new SqlCommand("SELECT [User ID] FROM [User] WHERE Name = @UserName", connection)) - { - command.Parameters.AddWithValue("UserName", userName); - - connection.Open(); - - return Convert.ToInt32(command.ExecuteScalar()); - } - } - } - - public static void WriteUserPasswordAndSalt(string password, string salt, int userId) - { - using (var connection = new SqlConnection(ConnectionString)) - { - using (var command = new SqlCommand("INSERT INTO [Login]([Password Hash], [Salt Hash], [User Key]) VALUES (@PasswordHash, @SaltHash, @UserKey)", connection)) - { - command.Parameters.AddWithValue("PasswordHash", password); - command.Parameters.AddWithValue("SaltHash", salt); - command.Parameters.AddWithValue("UserKey", userId); - - connection.Open(); - - command.ExecuteNonQuery(); - } - } - } - - public static void SetUserSessionToken(string userName, string sessionToken, DateTime expirationDate, string userAgent, string ipAddress, bool isResetToken = false) - { - using (var connection = new SqlConnection(ConnectionString)) - { - using (var command = new SqlCommand("InsertSessionToken", connection) { CommandType = CommandType.StoredProcedure }) - { - command.Parameters.AddWithValue("SessionToken", sessionToken); - command.Parameters.AddWithValue("ExpirationDate", expirationDate); - command.Parameters.AddWithValue("UserName", userName); - command.Parameters.AddWithValue("UserAgent", userAgent); - command.Parameters.AddWithValue("IpAddress", ipAddress); - command.Parameters.AddWithValue("PendingReset", isResetToken); - - connection.Open(); - - command.ExecuteNonQuery(); - } - } - } - - public static int RegisterNewUser(string userName, string email, string passwordHash, string saltHash, string sessionToken, DateTime expirationDate, string userAgent, string ipAddress) - { - using (var connection = new SqlConnection(ConnectionString)) - { - using (var command = new SqlCommand("RegisterNewUser", connection) { CommandType = CommandType.StoredProcedure }) - { - command.Parameters.AddWithValue("UserName", userName); - command.Parameters.AddWithValue("Email", email); - command.Parameters.AddWithValue("Password", passwordHash); - command.Parameters.AddWithValue("Salt", saltHash); - command.Parameters.AddWithValue("SessionToken", sessionToken); - command.Parameters.AddWithValue("ExpirationDate", expirationDate); - command.Parameters.AddWithValue("UserAgent", userAgent); - command.Parameters.AddWithValue("IpAddress", ipAddress); - - connection.Open(); - - var result = command.ExecuteScalar(); - - return Convert.ToInt32(result); - } - } - } - - public static bool IsSessionTokenValid(string sessionToken)//, string userName) - { - using (var connection = new SqlConnection(ConnectionString)) - { - using (var command = new SqlCommand("ValidateSessionToken", connection) { CommandType = CommandType.StoredProcedure }) - { - command.Parameters.AddWithValue("SessionToken", sessionToken); - - connection.Open(); - - var result = command.ExecuteScalar(); - - return Convert.ToBoolean(result); - } - } - } - - public static void DestroySession(string sessionToken) - { - using (var connection = new SqlConnection(ConnectionString)) - { - using (var command = new SqlCommand("DestroySession", connection) { CommandType = CommandType.StoredProcedure }) - { - command.Parameters.AddWithValue("SessionToken", sessionToken); - - connection.Open(); - - command.ExecuteNonQuery(); - } - } - } - - public static bool IsResetTokenValid(string token) - { - using (var connection = new SqlConnection(ConnectionString)) - { - using (var command = new SqlCommand("ValidateResetToken", connection) { CommandType = CommandType.StoredProcedure }) - { - command.Parameters.AddWithValue("SessionToken", token); - - connection.Open(); - - var reader = command.ExecuteScalar(); - - return Convert.ToBoolean(reader); - } - } - } - - //public static void ResetPassword(string email) - //{ - // using (var connection = new SqlConnection(ConnectionString)) - // { - // using (var command = new SqlCommand("DestroySession", connection) { CommandType = CommandType.StoredProcedure }) - // { - // command.Parameters.AddWithValue("SessionToken", sessionToken); - - // connection.Open(); - - // command.ExecuteNonQuery(); - // } - // } - //} } }