ASP.NET Core でのデータ保護に関する非 DI 対応シナリオ

作成者: Rick Anderson

通常、ASP.NET Core のデータ保護システムはサービス コンテナーに追加され、依存関係の挿入 (DI) を介して依存するコンポーネントによって使われます。 ただし、既存のアプリにシステムをインポートする場合などに実行できない、または望ましくない場合があります。

このようなシナリオをサポートするために、Microsoft.AspNetCore.DataProtection.Extensions パッケージには具象型である DataProtectionProvider が用意されています。これを利用すると、DI に頼らずにデータ保護を簡単に使うことができます。 DataProtectionProvider 型には IDataProtectionProvider が実装されています。 DataProtectionProvider を構築するには、次のコード サンプルのように、プロバイダーの暗号キーが格納される場所を示す DirectoryInfo インスタンスを指定するだけで済みます。

using System;
using System.IO;
using Microsoft.AspNetCore.DataProtection;

public class Program
{
    public static void Main(string[] args)
    {
        // Get the path to %LOCALAPPDATA%\myapp-keys
        var destFolder = Path.Combine(
            System.Environment.GetEnvironmentVariable("LOCALAPPDATA"),
            "myapp-keys");

        // Instantiate the data protection system at this folder
        var dataProtectionProvider = DataProtectionProvider.Create(
            new DirectoryInfo(destFolder));

        var protector = dataProtectionProvider.CreateProtector("Program.No-DI");
        Console.Write("Enter input: ");
        var input = Console.ReadLine();

        // Protect the payload
        var protectedPayload = protector.Protect(input);
        Console.WriteLine($"Protect returned: {protectedPayload}");

        // Unprotect the payload
        var unprotectedPayload = protector.Unprotect(protectedPayload);
        Console.WriteLine($"Unprotect returned: {unprotectedPayload}");

        Console.WriteLine();
        Console.WriteLine("Press any key...");
        Console.ReadKey();
    }
}

/*
 * SAMPLE OUTPUT
 *
 * Enter input: Hello world!
 * Protect returned: CfDJ8FWbAn6...ch3hAPm1NJA
 * Unprotect returned: Hello world!
 *
 * Press any key...
*/

DataProtectionProvider 具象型の既定では、生のキーはファイル システムに永続化される前に暗号化されません。 これは、開発者がネットワーク共有を指しても、データ保護システムによって自動的に適切な at-rest キー暗号化メカニズムを推測できないシナリオをサポートするためです。

さらに、DataProtectionProvider 具象型の既定では、アプリの分離は行われません。 同じキー ディレクトリを使うすべてのアプリは、目的パラメーターが一致する限り、ペイロードを共有できます。

DataProtectionProvider コンストラクターは、システムの動作を調整するために使える省略可能な構成コールバックを受け取ります。 次のサンプルは、SetApplicationName を明示的に呼び出して分離を復元する方法を示しています。 また、このサンプルは、Windows DPAPI を使って永続化キーを自動的に暗号化するようにシステムを構成しています。 ディレクトリが UNC 共有を指している場合は、関連するすべてのマシンに共有証明書を配布し、ProtectKeysWithCertificate を呼び出して証明書ベースの暗号化を使うようにシステムを構成することができます。

using System;
using System.IO;
using Microsoft.AspNetCore.DataProtection;

public class Program
{
    public static void Main(string[] args)
    {
        // Get the path to %LOCALAPPDATA%\myapp-keys
        var destFolder = Path.Combine(
            System.Environment.GetEnvironmentVariable("LOCALAPPDATA"),
            "myapp-keys");

        // Instantiate the data protection system at this folder
        var dataProtectionProvider = DataProtectionProvider.Create(
            new DirectoryInfo(destFolder),
            configuration =>
            {
                configuration.SetApplicationName("my app name");
                configuration.ProtectKeysWithDpapi();
            });

        var protector = dataProtectionProvider.CreateProtector("Program.No-DI");
        Console.Write("Enter input: ");
        var input = Console.ReadLine();

        // Protect the payload
        var protectedPayload = protector.Protect(input);
        Console.WriteLine($"Protect returned: {protectedPayload}");

        // Unprotect the payload
        var unprotectedPayload = protector.Unprotect(protectedPayload);
        Console.WriteLine($"Unprotect returned: {unprotectedPayload}");

        Console.WriteLine();
        Console.WriteLine("Press any key...");
        Console.ReadKey();
    }
}

ヒント

DataProtectionProvider 具象型のインスタンスは、作成するために高いコストがかかります。 アプリでこの型のインスタンスを複数保守し、そのすべてに同じキー ストレージ ディレクトリが使われている場合、アプリのパフォーマンスが低下する可能性があります。 DataProtectionProvider 型を使う場合は、この型を一度作成し、できるだけ再利用することをお勧めします。 DataProtectionProvider 型とそこから作成されたすべての IDataProtector インスタンスは、複数の呼び出し元に対してスレッドセーフです。