New: Migrate user passwords to Pbkdf2
This commit is contained in:
parent
fe293ff4c3
commit
269e72a219
|
@ -1,4 +1,4 @@
|
|||
using System;
|
||||
using System;
|
||||
using NzbDrone.Core.Datastore;
|
||||
|
||||
namespace NzbDrone.Core.Authentication
|
||||
|
@ -8,5 +8,7 @@ namespace NzbDrone.Core.Authentication
|
|||
public Guid Identifier { get; set; }
|
||||
public string Username { get; set; }
|
||||
public string Password { get; set; }
|
||||
public string Salt { get; set; }
|
||||
public int Iterations { get; set; }
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
using System;
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Security.Cryptography;
|
||||
using System.Xml.Linq;
|
||||
using Microsoft.AspNetCore.Cryptography.KeyDerivation;
|
||||
using NzbDrone.Common.Disk;
|
||||
using NzbDrone.Common.EnvironmentInfo;
|
||||
using NzbDrone.Common.Extensions;
|
||||
|
@ -25,6 +27,10 @@ namespace NzbDrone.Core.Authentication
|
|||
private readonly IAppFolderInfo _appFolderInfo;
|
||||
private readonly IDiskProvider _diskProvider;
|
||||
|
||||
private static readonly int ITERATIONS = 10000;
|
||||
private static readonly int SALT_SIZE = 128 / 8;
|
||||
private static readonly int NUMBER_OF_BYTES = 256 / 8;
|
||||
|
||||
public UserService(IUserRepository repo, IAppFolderInfo appFolderInfo, IDiskProvider diskProvider)
|
||||
{
|
||||
_repo = repo;
|
||||
|
@ -34,12 +40,15 @@ namespace NzbDrone.Core.Authentication
|
|||
|
||||
public User Add(string username, string password)
|
||||
{
|
||||
return _repo.Insert(new User
|
||||
{
|
||||
Identifier = Guid.NewGuid(),
|
||||
Username = username.ToLowerInvariant(),
|
||||
Password = password.SHA256Hash()
|
||||
});
|
||||
var user = new User
|
||||
{
|
||||
Identifier = Guid.NewGuid(),
|
||||
Username = username.ToLowerInvariant()
|
||||
};
|
||||
|
||||
SetUserHashedPassword(user, password);
|
||||
|
||||
return _repo.Insert(user);
|
||||
}
|
||||
|
||||
public User Update(User user)
|
||||
|
@ -58,7 +67,7 @@ namespace NzbDrone.Core.Authentication
|
|||
|
||||
if (user.Password != password)
|
||||
{
|
||||
user.Password = password.SHA256Hash();
|
||||
SetUserHashedPassword(user, password);
|
||||
}
|
||||
|
||||
user.Username = username.ToLowerInvariant();
|
||||
|
@ -85,7 +94,20 @@ namespace NzbDrone.Core.Authentication
|
|||
return null;
|
||||
}
|
||||
|
||||
if (user.Password == password.SHA256Hash())
|
||||
if (user.Salt.IsNullOrWhiteSpace())
|
||||
{
|
||||
// If password matches stored SHA256 hash, update to salted hash and verify.
|
||||
if (user.Password == password.SHA256Hash())
|
||||
{
|
||||
SetUserHashedPassword(user, password);
|
||||
|
||||
return Update(user);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
if (VerifyHashedPassword(user, password))
|
||||
{
|
||||
return user;
|
||||
}
|
||||
|
@ -98,6 +120,43 @@ namespace NzbDrone.Core.Authentication
|
|||
return _repo.FindUser(identifier);
|
||||
}
|
||||
|
||||
private User SetUserHashedPassword(User user, string password)
|
||||
{
|
||||
var salt = GenerateSalt();
|
||||
|
||||
user.Iterations = ITERATIONS;
|
||||
user.Salt = Convert.ToBase64String(salt);
|
||||
user.Password = GetHashedPassword(password, salt, ITERATIONS);
|
||||
|
||||
return user;
|
||||
}
|
||||
|
||||
private byte[] GenerateSalt()
|
||||
{
|
||||
var salt = new byte[SALT_SIZE];
|
||||
RandomNumberGenerator.Create().GetBytes(salt);
|
||||
|
||||
return salt;
|
||||
}
|
||||
|
||||
private string GetHashedPassword(string password, byte[] salt, int iterations)
|
||||
{
|
||||
return Convert.ToBase64String(KeyDerivation.Pbkdf2(
|
||||
password: password,
|
||||
salt: salt,
|
||||
prf: KeyDerivationPrf.HMACSHA512,
|
||||
iterationCount: iterations,
|
||||
numBytesRequested: NUMBER_OF_BYTES));
|
||||
}
|
||||
|
||||
private bool VerifyHashedPassword(User user, string password)
|
||||
{
|
||||
var salt = Convert.FromBase64String(user.Salt);
|
||||
var hashedPassword = GetHashedPassword(password, salt, user.Iterations);
|
||||
|
||||
return user.Password == hashedPassword;
|
||||
}
|
||||
|
||||
public void Handle(ApplicationStartedEvent message)
|
||||
{
|
||||
if (_repo.All().Any())
|
||||
|
|
|
@ -0,0 +1,16 @@
|
|||
using FluentMigrator;
|
||||
using NzbDrone.Core.Datastore.Migration.Framework;
|
||||
|
||||
namespace NzbDrone.Core.Datastore.Migration
|
||||
{
|
||||
[Migration(174)]
|
||||
public class add_salt_to_users : NzbDroneMigrationBase
|
||||
{
|
||||
protected override void MainDbUpgrade()
|
||||
{
|
||||
Alter.Table("Users")
|
||||
.AddColumn("Salt").AsString().Nullable()
|
||||
.AddColumn("Iterations").AsInt32().Nullable();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -5,6 +5,7 @@
|
|||
<ItemGroup>
|
||||
<PackageReference Include="Dapper" Version="2.0.123" />
|
||||
<PackageReference Include="MailKit" Version="2.10.1" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Cryptography.KeyDerivation" Version="6.0.6" />
|
||||
<PackageReference Include="Servarr.FFMpegCore" Version="4.7.0-26" />
|
||||
<PackageReference Include="Servarr.FFprobe" Version="5.0.1.91" />
|
||||
<PackageReference Include="System.Memory" Version="4.5.4" />
|
||||
|
|
Loading…
Reference in New Issue