web-dev-qa-db-ja.com

Asp.NET CoreおよびFramework全体のデータ保護プロバイダー(パスワードリセットリンクの生成)

(DataProtectionProvider に関連するこの問題に遭遇しています。最初に2つの.NET Frameworkプロジェクトしかありませんでした。現在、.NET Coreプロジェクトが追加されているため、次の方法を混乱させることになります。パスワードのリセットリンクを生成する.NET Frameworkプロジェクトから、.NET Coreプロジェクトで使用します。どちらも同じデータベースとユーザーテーブルを使用しており、相互に互換性があります。 .NET Frameworkは、まだコードファーストデータベース生成の主要プロジェクトです。

両方の.NET Frameworksプロジェクトで、次のコードを含む1つの共有コードベースを使用します。

//not sure where I got this from but it is part of the solution for solving
//password link generating and using in two different applications.
public class MachineKeyProtectionProvider : IDataProtectionProvider
{
    public IDataProtector Create(params string[] purposes)
    {
        return new MachineKeyDataProtector(purposes);
    }
}

public class MachineKeyDataProtector : IDataProtector
{
    private readonly string[] _purposes;

    public MachineKeyDataProtector(string[] purposes)
    {
        _purposes = purposes;
    }

    public byte[] Protect(byte[] userData)
    {
        return MachineKey.Protect(userData, _purposes);
    }

    public byte[] Unprotect(byte[] protectedData)
    {
        return MachineKey.Unprotect(protectedData, _purposes);
    }
}

次に、ユーザーリポジトリ内で:

    private readonly UserManager<ApplicationUser> _userManager = null;
    private readonly RoleManager<IdentityRole> _roleManager = null;

    internal static IDataProtectionProvider DataProtectionProvider { get; private set; }
    public UserRepository(DatabaseContext dbContext)
    {
        _userManager = new UserManager<ApplicationUser>(new UserStore<ApplicationUser>(dbContext));
        _roleManager = new RoleManager<IdentityRole>(new RoleStore<IdentityRole>(dbContext));
        _userManager.UserValidator = new UserValidator<ApplicationUser>(_userManager) { AllowOnlyAlphanumericUserNames = false };
        if (DataProtectionProvider == null)
        {
            DataProtectionProvider = new MachineKeyProtectionProvider();
        }
        _userManager.UserTokenProvider = new DataProtectorTokenProvider<ApplicationUser, string>(DataProtectionProvider.Create("Identity"));
    }

両方の.NET Frameworkプロジェクトに<machineKey> セットする。その後、私は簡単に使用できます:

    public string GeneratePasswordResetCode(string userId)
    {
        return _userManager.GeneratePasswordResetToken(userId);
    }

    public void ChangeUserPassword(string oldPassword, string newPassword)
    {
        string id = InfrastructureUserHelper.User.GetUserId();
        IdentityResult result = _userManager.ChangePassword(id, oldPassword, newPassword);
        ...
    }

そのため、.NET Coreプロジェクトが追加されました。これには、独自のパスワードリセットメカニズムがすでに機能していますが、すべての自動化ジョブは.NET Frameworkプロジェクトの1つから送信されます。理由は、ユーザーが自動的に作成されたアカウントのパスワードを設定する必要があるためです。

どうすればよいですか?私はこれを見てきました: https://docs.Microsoft.com/en-us/aspnet/core/security/data-protection/configuration/overview?tabs=aspnetcore2x そしてこれ:- ASP.NET Core 2.0でmachineKeyを実装する方法

しかし、私はシンプルで簡単なソリューションが何であるかを実際には理解できません。追加のredisサーバーの作成は避けたいです。マシンキーに似たものがその仕事をします。 このドキュメント から始めようとしましたが、.NET Coreプロジェクトにどの部分が含まれ、.NET Frameworkプロジェクトにどの部分が含まれるのか、実際にはわかりませんでした。

私はこれまで運がなくてもこの部分をいじってみました:

    public void ConfigureServices(IServiceCollection services)
    {
        services.AddDataProtection().SetApplicationName("Identity")
            .SetDefaultKeyLifetime(TimeSpan.FromDays(60))
            .ProtectKeysWithDpapi();

        services.AddIdentity<ApplicationUser, ApplicationRole>().AddDefaultTokenProviders();
    }

編集:最終的にはこのためにBlob Storageを使用できます。このページを参照してください。 https://docs.Microsoft.com/en- us/aspnet/core/security/data-protection/configuration/overview?tabs = aspnetcore2x

これを追加すると少しは役立つようですが、どちらのアプリも正常に動作しますが、.NET Frameworkプロジェクトでは正しいDataProtectionProviderを使用しません。 UserManagerはそれを見つけることができません(ITokenProviderエラーはありません)。

最終的に私はトークンをあきらめてユーザーデータベースに保存しましたが、完全に理想的ではありませんが、多くの時間がドキュメント化されていないものを解決するために費やしていました。マイクロソフトの場合は-1。

11
CularBytes

データ保護APIは、.NET Frameworkと.NET Coreで同じように機能します。

マシンキーの代わりに、古いマシンキーアプローチではなく、X509証明書でキーを暗号化します。暗号化は純粋に内部使用を目的としているため、自己生成証明書は問題ありません。

重要:サーバーを制御する場合は、証明書を証明書ストアにインストールする必要があります。 X509証明書インスタンスを受け入れるProtectWithCertificateオーバーロードを使用することには、ばかげた、文書化されていないキャッチがあります。そのインスタンスは、encryptにしか使用できません。証明書がストアにない場合、復号化は失敗します。 Microsoftは、これは「基礎となるフレームワーク」の制限であると主張していますが、回避策はあります(複雑ではありません)。私は this のバリエーションを使用しており、証明書はAzure Key Vaultにシリアル化されているため、個々のサーバーに触れる必要はありません。

また、すべてのサーバーがデータにアクセスできるようにするには、いずれかのDPAPI永続化オプションを指定する必要があります。 Azureを使用しているため、以下のコードではblobストレージを使用していますが、独自のサーバーを実行している場合、これはPersistKeysToFileSystemを介したネットワーク共有である可能性があります。

ConfigureServicesの設定は次のようになります。

var x509 = GetDpApiCert(); // library utility
var container = GetBlobStorageRef(); // library

services.AddDataProtection()
    .SetApplicationName(appconfig["DpapiSiteName"])
    .ProtectKeysWithProvidedCertificate(x509)
    .PersistKeysToAzureBlobStorage(container, appconfig["DpapiFileName"]);

これが、証明書を生成するための私のPowershellスクリプトです。

[CmdletBinding()]
param(
    [Parameter(Mandatory=$true)][string]$password = "",
    [Parameter(Mandatory=$true)][string]$rootDomain = ""
)

$cwd = Convert-Path .
$CerFile = "$cwd\aspnet_dpapi.cer"
$PfxFile = "$cwd\aspnet_dpapi.pfx"

# abort if files exist
if((Test-Path($PfxFile)) -or (Test-Path($CerFile)))
{
    Write-Warning "Failed, aspnet_dpapi already exists in $cwd"
    Exit
}

$cert = New-SelfSignedCertificate `
        -Subject $rootDomain `
        -DnsName $rootDomain `
        -FriendlyName "ASP.NET Data Protection $rootDomain" `
        -NotBefore (Get-Date) `
        -NotAfter (Get-Date).AddYears(10) `
        -CertStoreLocation "cert:CurrentUser\My" `
        -KeyAlgorithm RSA `
        -Provider "Microsoft Enhanced RSA and AES Cryptographic Provider" `
        -KeyLength 2048 `
        -KeyUsage KeyEncipherment, DataEncipherment
        # -HashAlgorithm SHA256 `
        # -Type Custom,DocumentEncryptionCert `
        # -TextExtension @("2.5.29.37={text}1.3.6.1.5.5.7.3.1")

$store = 'Cert:\CurrentUser\My\' + ($cert.ThumbPrint)  
$securePass = ConvertTo-SecureString -String $password -Force -AsPlainText

Export-Certificate -Cert $store -FilePath $CerFile
Export-PfxCertificate -Cert $store -FilePath $PfxFile -Password $securePass
8
McGuireV10