繁体   English   中英

如何使用哈希密码对用户进行身份验证?

[英]How to authenticate user with hashed password?

我正在尝试使用实现安全密码存储,但docsTestWorkshashesDontMatchwrongPassword似乎总是正确的。 我不认为他们应该是。 我做错了什么(或很多事情)吗?

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);
}

我觉得这过于简单化了,我可能会遗漏很多东西......

我正在使用的 package:

dotnet add package Sodium.Core

我编写了一些使用System.Security.Cryptography命名空间的扩展来简化这种工作。

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

添加到您的项目后, using System.Security.Cryptography引用命名空间,然后您可以 hash 并比较任何字符串中的 hash。

例如:

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.");

我最终得到以下结果:

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重载生成随机盐。


然后我最终添加了一个助手 class:

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);
}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM