ASP.NET Core のコンシューマー API の概要

IDataProtectionProvider および IDataProtector インターフェイスは、コンシューマーがデータ保護システムを使うための基本的なインターフェイスです。 これらは Microsoft.AspNetCore.DataProtection.Abstractions パッケージ内にあります。

IDataProtectionProvider

プロバイダー インターフェイスは、データ保護システムのルートを表します。 データの保護または保護解除に直接使うことはできません。 代わりに、コンシューマーは IDataProtectionProvider.CreateProtector(purpose) を呼び出して IDataProtector への参照を取得する必要があります。この目的は、意図したコンシューマーのユース ケースを示す文字列です。 このパラメーターの意図と適切な値の選択方法の詳細については、「目的文字列」を参照してください。

IDataProtector

プロテクター インターフェイスは CreateProtector の呼び出しによって返されます。コンシューマーはこのインターフェイスを使って保護と保護解除の操作を行うことができます。

データの一部を保護するには、Protect メソッドにデータを渡します。 基本インターフェイスには、byte[] -> byte[] を変換するメソッドが定義されていますが、string -> string を変換するオーバーロードもあります (拡張メソッドとして提供されています)。 この 2 つのメソッドによって提供されるセキュリティは同じです。そのため、開発者はユース ケースに適したオーバーロードを選ぶ必要があります。 どちらのオーバーロードを選んでも、Protect メソッドから返される値は保護されている (暗号化され、改ざんが防止されている) ため、信頼されていないクライアントに対してアプリケーションから送信することができます。

既に保護されているデータの保護を解除するには、保護されているデータを Unprotect メソッドに渡します (開発者の利便性を考慮して byte[] ベースと文字列ベースのオーバーロードが用意されています)。保護されたペイロードが、この同じ IDataProtector 上の Protect の以前の呼び出しによって生成された場合、Unprotect メソッドからは元の保護されていないペイロードが返されます。 保護されたペイロードが改ざんされている場合、または別の IDataProtector によって生成された場合は、Unprotect メソッドから CryptographicException がスローされます。

IDataProtector が同じまたは異なるという概念は、前述の目的の概念につながります。 2 つの IDataProtector インスタンスが同じルートの IDataProtectionProvider から生成されても、IDataProtectionProvider.CreateProtector の呼び出しでは異なる目的文字列を経由した場合、それらは異なるプロテクターと見なされ、いずれかから、もう一方によって生成されたペイロードの保護を解除することはできません。

これらのインターフェイスの使用

DI 対応コンポーネントの場合、コンポーネントがコンストラクターで IDataProtectionProvider パラメーターを受け取り、コンポーネントのインスタンスが作成されたときに DI システムによって自動的にこのサービスが提供されるという使い方が想定されています。

Note

一部のアプリケーション (コンソール アプリケーションや ASP.NET 4.x アプリケーションなど) は、DI 対応ではないため、ここで説明したメカニズムを使えない場合があります。 このようなシナリオの場合、DI を経由せずに IDataProtection プロバイダーのインスタンスを取得する方法については、「DI に対応しないシナリオ」のドキュメントを参照してください。

次のサンプルは、3 つの概念を示しています。

  1. データ保護システムをサービス コンテナーに追加する、

  2. DI を使って IDataProtectionProvider のインスタンスを受け取る、および

  3. IDataProtectionProvider から IDataProtector を作成し、それを使ってデータの保護と保護解除を行う。

コンソール アプリ

using System;
using Microsoft.AspNetCore.DataProtection;
using Microsoft.Extensions.DependencyInjection;

public class Program
{
    public static void Main(string[] args)
    {
        // add data protection services
        var serviceCollection = new ServiceCollection();
        serviceCollection.AddDataProtection();
        var services = serviceCollection.BuildServiceProvider();

        // create an instance of MyClass using the service provider
        var instance = ActivatorUtilities.CreateInstance<MyClass>(services);
        instance.RunSample();
    }

    public class MyClass
    {
        IDataProtector _protector;

        // the 'provider' parameter is provided by DI
        public MyClass(IDataProtectionProvider provider)
        {
            _protector = provider.CreateProtector("Contoso.MyClass.v1");
        }

        public void RunSample()
        {
            Console.Write("Enter input: ");
            string input = Console.ReadLine();

            // protect the payload
            string protectedPayload = _protector.Protect(input);
            Console.WriteLine($"Protect returned: {protectedPayload}");

            // unprotect the payload
            string unprotectedPayload = _protector.Unprotect(protectedPayload);
            Console.WriteLine($"Unprotect returned: {unprotectedPayload}");
        }
    }
}

/*
 * SAMPLE OUTPUT
 *
 * Enter input: Hello world!
 * Protect returned: CfDJ8ICcgQwZZhlAlTZT...OdfH66i1PnGmpCR5e441xQ
 * Unprotect returned: Hello world!
 */

Web アプリ

Program.cs 内で AddDataProtection(IServiceCollection, Action<DataProtectionOptions>) を呼び出します:

var builder = WebApplication.CreateBuilder(args);

// Add services to the container.
builder.Services.AddControllersWithViews();
builder.Services.AddDataProtection();

var app = builder.Build();

次の強調表示されたコードは、コントローラーでの IDataProtector を使用する方法を示しています:

public class HomeController : Controller
{
    private readonly IDataProtector _dataProtector;

    public HomeController(IDataProtectionProvider dataProtectionProvider)
    {
        _dataProtector = dataProtectionProvider.CreateProtector("HomeControllerPurpose");
    }

    // ...

    public IActionResult Privacy()
    {
        // The original data to protect
        string originalData = "original data";

        // Protect the data (encrypt)
        string protectedData = _dataProtector.Protect(originalData);
        Console.WriteLine($"Protected Data: {protectedData}");

        // Unprotect the data (decrypt)
        string unprotectedData = _dataProtector.Unprotect(protectedData);
        Console.WriteLine($"Unprotected Data: {unprotectedData}");

        return View();
    }
    
    // ...

パッケージ Microsoft.AspNetCore.DataProtection.Abstractions には開発者の便宜のため拡張メソッド GetDataProtector が含まれています。 これには、サービス プロバイダーから IDataProtectionProvider を取得することと IDataProtectionProvider.CreateProtector を呼び出すことが 1 つの操作としてカプセル化されています。 次のサンプルは、この使用例を示しています:

using System;
using Microsoft.AspNetCore.DataProtection;
using Microsoft.Extensions.DependencyInjection;
 
public class Program
{
    public static void Main(string[] args)
    {
        // add data protection services
        var serviceCollection = new ServiceCollection();
        serviceCollection.AddDataProtection();
        var services = serviceCollection.BuildServiceProvider();
 
        // get an IDataProtector from the IServiceProvider
        var protector = services.GetDataProtector("Contoso.Example.v2");
        Console.Write("Enter input: ");
        string input = Console.ReadLine();
 
        // protect the payload
        string protectedPayload = protector.Protect(input);
        Console.WriteLine($"Protect returned: {protectedPayload}");
 
        // unprotect the payload
        string unprotectedPayload = protector.Unprotect(protectedPayload);
        Console.WriteLine($"Unprotect returned: {unprotectedPayload}");
    }
}

ヒント

IDataProtectionProviderIDataProtector のインスタンスは、複数の呼び出し元に対してスレッドセーフです。 これは、コンポーネントが CreateProtector への呼び出しによって IDataProtector への参照を取得すると、その参照が ProtectUnprotect の複数の呼び出しに使用されることを意図しています。 保護されたペイロードを検証または解読できない場合、Unprotect を呼び出すと CryptographicException がスローされます。 一部のコンポーネントでは、保護解除操作中にエラーを無視することが必要な場合があります。認証 cookie を読み取るコンポーネントでは、このエラーを処理し、要求を完全に失敗させるのではなく、cookie がまったくない場合と同様に要求を処理することができます。 この動作を必要とするコンポーネントは、すべての例外を飲み込むのではなく、CryptographicException を明示的にキャッチする必要があります。