Как аутентифицировать пользователя с помощью хешированного пароля? - PullRequest
0 голосов
/ 03 мая 2020

Я пытаюсь реализовать безопасное хранение паролей, используя , но docsTestWorks, hashesDontMatch и wrongPassword кажутся всегда true. Я не думаю, что они должны быть. Я что-то делаю (или многие вещи в этом отношении) неправильно?

public async Task<IUser> Authenticate(ICredentials credentials)
{
    using var connection = _databaseGateway.Connection;
    connection.Open();

    const string PASSWORD = "Correct Horse Battery Staple";
    var hash = PasswordHash
        .ScryptHashString(PASSWORD);
    var docsTestWorks = PasswordHash.ScryptHashStringVerify(hash, PASSWORD);

    var incomingHash = PasswordHash
        .ScryptHashString(credentials.Password);
    var test1 = PasswordHash
        .ScryptHashString("myPassword");
    var test2 = PasswordHash
        .ScryptHashString("myPassword");
    var hashesDontMatch = test1 != test2;
    var users = await connection.GetAllAsync<User>();
    var existingUser = users
        .Single(u => u.Username == credentials.Username);
    var wrongPassword = !PasswordHash
        .ScryptHashStringVerify(existingUser.PasswordHash, credentials.Password);

    if (wrongPassword)
        throw new AuthenticationException(Error.WrongPassword,
            "Error: Incorrect Password.");

    return AddToken(existingUser);
}

Я чувствую, что это слишком упрощенно c, и я могу упустить множество вещей ...

Пакет, который я использую:

dotnet add package Sodium.Core

Ответы [ 2 ]

0 голосов
/ 03 мая 2020

Я получил следующее:

public async Task<IUser> Authenticate(ICredentials credentials)
{
    using var connection = _databaseGateway.Connection;
    connection.Open();
    var users = await connection.GetAllAsync<User>();
    const int expectedUserCount = 1;
    var existingUsers = users
        .Where(u => u.Username == credentials.Username);

    if (existingUsers.Count() != expectedUserCount)
        throw new AuthenticationException(Error.UserDoesNotExist,
            "Error: There is no single user exists with the username given.");
    var existingUser = existingUsers.Single();

    var salt = existingUser.PasswordSalt;
    // TURN ON FOR REGISTERING USER PW
    // salt = PasswordHash.ScryptGenerateSalt();
    var password = Encoding.UTF8.GetBytes(credentials.Password);
    var hash = PasswordHash.ScryptHashBinary(password, salt);
    // TURN ON FOR REGISTERING USER PW
    // await connection.ExecuteAsync(
    //    "UPDATE user SET PasswordHash=@hash, PasswordSalt=@salt WHERE UserName=@username;",
    //    new {hash, salt, username = credentials.Username});

    var wrongPassword = !hash.SequenceEqual(existingUser.PasswordHash);

    if (wrongPassword)
        throw new AuthenticationException(Error.WrongPassword,
            "Error: Incorrect Password.");

    return AddToken(existingUser);
}

Подозрение здесь было правильным, я думаю. Кажется, что каждый раз генерируется случайная соль с string перегрузками метода (ов).


Затем я добавил вспомогательный класс:

using System.Collections.Generic;
using System.Linq;
using System.Text;
using Sodium;

namespace Services
{
    public static class HashingService
    {
        public static bool IsGeneratedBy(
            this IEnumerable<byte> existingHash,
            string plaintextPassword,
            byte[] salt
        )
        {
            var password = Encoding.UTF8.GetBytes(plaintextPassword);
            var generatedHash = PasswordHash.ScryptHashBinary(password, salt);
            var passwordIsCorrect = generatedHash.SequenceEqual(existingHash);
            return passwordIsCorrect;
        }
    }
}

и использовал его как:

public async Task<IUser> Authenticate(ICredentials credentials)
{
    using var connection = _databaseGateway.Connection;
    connection.Open();
    var users = await connection.GetAllAsync<User>();
    const int expectedUserCount = 1;
    var existingUser = users
        .Single(u => u.Username == credentials.Username);

    var wrongPassword = !existingUser.PasswordHash.IsGeneratedBy(
        credentials.Password, existingUser.PasswordSalt);

    if (wrongPassword)
        throw new AuthenticationException(Error.WrongPassword,
            "Error: Incorrect Password.");

    return AddToken(existingUser);
}
0 голосов
/ 03 мая 2020

Я написал несколько расширений, которые используют пространство имен System.Security.Cryptography для упрощения такого рода работы.

https://github.com/jscarle/Crypto.NET/blob/master/Crypto.NET/System.Security.Cryptography.Extensions.cs

После добавления в ваш проект, обратитесь к пространство имен с using System.Security.Cryptography, тогда вы можете ха sh и сравнить ха sh из любой строки.

Например:

string password1 = "some password";
string hash1 = password1.Hash();

string password2 = "some other password";
string hash2 = password2.Hash();

string password3 = "some password";
string hash3 = password3.Hash();

Console.WriteLine($"{password1} = {hash1}");
Console.WriteLine($"{password2} = {hash2}");
Console.WriteLine($"{password3} = {hash3}");

if (password1.CompareToHash(hash2))
    Console.WriteLine("hash1 and hash2 match.");
else
    Console.WriteLine("hash1 and hash2 do not match.");

if (password1.CompareToHash(hash3))
    Console.WriteLine("hash1 and hash3 match.");
else
    Console.WriteLine("hash1 and hash3 do not match.");
...