Функциональность входа в систему с использованием имени, фамилии и пароля с использованием C #

Резюме

Я завершил создание функции безопасного входа в систему. Я использовал 1 ресурс, чтобы помочь мне с безопасностью. Раздел безопасности был скопирован и вставлен, но реализован в классе, чтобы удовлетворить его по-своему, но вся логика и свойства, команды и т. Д. — это моя работа. https://medium.com/@mehanix/lets-talk-security-salted-password-hashing-in-c-5460be5c3aae

Буду признателен за любой полезный обзор моего текущего кода, который у меня есть, от сообщества в тех областях, в которых я могу улучшить.

Текущий код

SecurePasswordHasher.cs

На данный момент не использую public static string Hash(string password) где бы то ни было, основное внимание уделяется проверке.

public static class SecurePasswordHasher
{

    public static string Hash(string password)
    {
        // Create salt
        byte[] salt;
        new RNGCryptoServiceProvider().GetBytes(salt = new byte[16]);

        // Create hash
        var pbkdf2 = new Rfc2898DeriveBytes(password, salt, 10000);

        byte[] hash = pbkdf2.GetBytes(20);
        byte[] hashBytes = new byte[36];
        Array.Copy(salt, 0, hashBytes, 0, 16);
        Array.Copy(hash, 0, hashBytes, 16, 20);

        return Convert.ToBase64String(hashBytes);
    }

    public static bool Verify(string savedPassword, string givenPassword)
    {
        byte[] hashBytes = Convert.FromBase64String(savedPassword);
        byte[] salt = new byte[16];
        Array.Copy(hashBytes, 0, salt, 0, 16);
        var pbkdf2 = new Rfc2898DeriveBytes(givenPassword, salt, 10000);
        byte[] hash = pbkdf2.GetBytes(20);

        int ok = 1;
        for (int i = 0; i < 20; i++)
        {
            if (hashBytes[i + 16] != hash[i])
            {
                ok = 0;
            }
        }

        if (ok == 1)
        {
            return true;
        }
        else
        {   
            return false;
        }

    }
}

LoginViewModel

public class LoginViewModel : BaseViewModel
{
   
    // User Properties
    private UserModel _User;
    public UserModel User
    {
        get { return _User; }
        set
        {
            _User = value;
            OnPropertyChanged(nameof(User));
        }
    }

    private string _FirstName;
    public string FirstName
    {
        get { return _FirstName; }
        set
        {
            _FirstName = value;
            User = new UserModel
            {
                FirstName = _FirstName,
                LastName = this.LastName,
                Password = this.Password
            };
            OnPropertyChanged(nameof(FirstName));
        }
    }

    private string _LastName;
    public string LastName
    {
        get { return _LastName; }
        set
        {
            _LastName = value;
            User = new UserModel
            {
                LastName = _LastName,
                FirstName = this.FirstName,
                Password = this.Password
            };
            OnPropertyChanged(nameof(LastName));
        }
    }


    private string _Password;
    public string Password
    {
        get { return _Password; }
        set
        {
            _Password = value;
            User = new UserModel
            {
                FirstName = this.FirstName,
                LastName = this.LastName,
                Password = _Password
            };
            OnPropertyChanged(nameof(Password));
        }
    }

    // Login Command
    public ICommand LoginCommand { get; set; }
    
    // Function
    private bool LoginFunction(object param)
    {
        // Get user credentials
        UserModel user = param as UserModel;
    
    
        // Veryify credentials have data to work with
        // Also, reason this method return a bool, because it prevents NullReferenceException.
        if (user == null)
        {
            MessageBox.ShowMessageBox("Credentials not specified");
            return false;
        }
        else if (user.FirstName == null || string.IsNullOrEmpty(user.FirstName))
        {
            MessageBox.ShowMessageBox("Firstname cannot be empty");
            return false;
        }
        else if (user.LastName == null || string.IsNullOrEmpty(user.LastName))
        {
            MessageBox.ShowMessageBox("Surname field cannot be empty");
            return false;
        }
        else if (user.Password == null || string.IsNullOrEmpty(user.Password))
        {
            MessageBox.ShowMessageBox("Password field cannot be empty");
            return false;
        }
        // Find Firstname and Surename in the database
        else
        {
            using (var conn = new MySqlConnection(ConnectionString.ConnString))
            {
                conn.Open();
    
                string query = @"SELECT
                                    *
                                    FROM USERS u
                                JOIN DEPARTMENT d
                                on d.id = u.departmentid
                                JOIN usergroup ug
                                ON ug.id = u.UserGroupID
                                WHERE FirstName = @Firstname AND Lastname = @LastName";
    
                var userDetails = conn.Query<UserModel>(query, new { FirstName = user.FirstName, LastName = user.LastName}).ToList();
    
                // If user is found, proceed to verification
                if(userDetails.Count() == 1)
                {
                    // Get password from the user
                    string savedPasswordHash = userDetails.First().Password;
    
                    bool verification = SecurePasswordHasher.Verify(savedPasswordHash, Password);
    
                    if (verification == true)
                    {
                        // Store Username
                        UserData.FullName = $"{user.FirstName} {user.LastName}";
    
                        ShowDashboard.ShowDashboard();
                        return true;
                    }
                    // Password is incorrect
                    else
                    {
                        MessageBox.ShowMessageBox("User not found");
                        return false;
                    }
                }
                // User with the given Firstname and surename is not found
                else
                {
                    MessageBox.ShowMessageBox("User not found");
                    return false;
                }
            }
        }
    }

    // Messagebox Interface
    public IMessageBoxService MessageBox { get; set; }

    // Open Dashboard Interface
    public IShowDashboardService ShowDashboard { get; set; }



    public LoginViewModel(IMessageBoxService messageBox, IShowDashboardService showDashboard)
    {
        this.MessageBox = messageBox;
        this.ShowDashboard = showDashboard;
        LoginCommand = new RelayCommand(param => LoginFunction(param));
    }
}

1 ответ
1

Сохранение пароля в string совершенно небезопасно.

Потому что string является неизменяемым объектом и может храниться в памяти неопределенный период времени. Это позволяет легко получить чистый пароль с помощью несложной операции обратного проектирования.

Кстати

Основное внимание уделяется проверке.

Ok

Переименован savedPassword потому что это хеш-пароль, а не чистый пароль.

public static bool Verify(string savedPasswordHash, string givenPassword)
{
    byte[] hashBytes = Convert.FromBase64String(savedPasswordHash);
    byte[] salt = hashBytes.Take(16).ToArray();
    byte[] hash = new Rfc2898DeriveBytes(givenPassword, salt, 10000).GetBytes(20);
  
    return hashBytes.Skip(16).SequenceEqual(hash);
}

Вот и все. Простой запрос Linq.

  • @ LV98 Вот мой подробный Русский ответ StackOverflow как безопасно использовать PasswordBox в WPF.

    — эспот


  • Мне просто интересно .. стоит ли иметь безопасность, если приложение используется только для внутренних целей.

    — LV98

  • @ LV98 по крайней мере стоит изучить безопасность.

    — эспот

  • Согласен .. Есть предложения, с чего я могу начать?

    — LV98

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *