ASP.NET Core データ保護の構成

データ保護システムを初期化すると、運用環境に基づく既定の設定が適用されます。 これらの設定は、1 台のコンピューター上で実行するアプリに適しています。 ただし、場合によっては、開発者が既定の設定を変更する必要があります。

  • アプリが複数のコンピューターに分散されている。
  • コンプライアンス上の理由から。

これらのシナリオに向けて、データ保護システムには豊富な構成 API が用意されています。

警告

構成ファイルと同様に、適切なアクセス許可を使ってデータ保護のキー リングを保護する必要があります。 restにキーを暗号化することもできますが、攻撃者による新しいキーの作成を防ぐことはできません。 その結果、アプリのセキュリティが影響を受けます。 構成ファイルを保護する方法と同様に、データ保護で構成される保存場所へのアクセスは、そのアプリ自体に制限する必要があります。 たとえば、キー リングをディスクに格納する場合は、ファイル システムのアクセス許可を使います。 Web アプリの実行に使う identity だけが、そのディレクトリへの読み取り、書き込み、作成アクセス権を持つようにする必要があります。 Azure Blob Storage を使う場合は、その Web アプリだけが BLOB ストアでの読み取り、書き込み、または新しいエントリの作成を行えるようにする必要があります。

拡張メソッド AddDataProtection から IDataProtectionBuilder が返されます。 IDataProtectionBuilder によって、データ保護オプションを構成するために連結できる各拡張メソッドが公開されます。

Note

この記事は、Docker コンテナー内で実行されるアプリ用に作成されました。 Docker コンテナーでは、アプリは常に同じパスを持つため、アプリケーション識別子も同じです。 複数の環境 (ローカル環境やデプロイ環境など) で実行する必要があるアプリは、その環境の既定のアプリケーション識別子を設定する必要があります。 複数の環境でアプリを実行する方法については、この記事では説明しません。

この記事で使うデータ保護拡張機能には、次の NuGet パッケージが必要です。

ProtectKeysWithAzureKeyVault

CLI を使って Azure にサインインします。次に例を示します。

az login

Azure Key Vault でキーを管理するには、Program.csProtectKeysWithAzureKeyVault を使ってシステムを構成します。 blobUriWithSasToken は、キー ファイルを格納する必要がある完全な URI です。 この URI には、SAS トークンがクエリ文字列パラメーターとして含まれている必要があります。

builder.Services.AddDataProtection()
    .PersistKeysToAzureBlobStorage(new Uri("<blobUriWithSasToken>"))
    .ProtectKeysWithAzureKeyVault(new Uri("<keyIdentifier>"), new DefaultAzureCredential());

アプリが KeyVault と通信し、自身を認可するには、Azure.Identity パッケージを追加する必要があります。

キー リングの保存場所を設定します (例: PersistKeysToAzureBlobStorage)。 この場所を設定する必要がある理由は、ProtectKeysWithAzureKeyVault の呼び出しで、キー リングの保存場所を含む自動データ保護設定を無効にする IXmlEncryptor が実装されているためです。 上記の例では、Azure Blob Storage を使ってキー リングを永続化しています。 詳細については、キー ストレージ プロバイダー:「Azure Storage」を参照してください。 PersistKeysToFileSystem を使ってローカルでキー リングを永続化することもできます。

keyIdentifier は、キーの暗号化に使うキー コンテナーのキー識別子です。 たとえば、contosokeyvault 内の dataprotection という名前のキー コンテナーに作成されたキーのキー識別子は https://contosokeyvault.vault.azure.net/keys/dataprotection/ です。 キー コンテナーに対して、[取得][キーの折り返しを解除]、および [キーを折り返す] のアクセス許可を付与してください。

ProtectKeysWithAzureKeyVault では次がオーバーロードされます。

アプリで古い Azure パッケージ (Microsoft.AspNetCore.DataProtection.AzureStorage と Microsoft.AspNetCore.DataProtection.AzureKeyVault) が使用されている場合、参照を "削除" して、Azure.Extensions.AspNetCore.DataProtection.BlobsAzure.Extensions.AspNetCore.DataProtection.Keys にアップグレードすることをお勧めします。 これらのパッケージでは、新しい更新プログラムを提供し、古いパッケージに関するいくつかの主要なセキュリティおよび安定性の問題に対処しています。

builder.Services.AddDataProtection()
    // This blob must already exist before the application is run
    .PersistKeysToAzureBlobStorage("<storageAccountConnectionString", "<containerName>", "<blobName>")
    // Removing this line below for an initial run will ensure the file is created correctly
    .ProtectKeysWithAzureKeyVault(new Uri("<keyIdentifier>"), new DefaultAzureCredential());

PersistKeysToFileSystem

%LOCALAPPDATA% の既定の場所ではなく UNC 共有にキーを格納するには、PersistKeysToFileSystem を使ってシステムを構成します。

builder.Services.AddDataProtection()
    .PersistKeysToFileSystem(new DirectoryInfo(@"\\server\share\directory\"));

警告

キーを永続化する場所を変更した場合、システムによってrestにキーが自動的に暗号化されることはなくなります。DPAPI が適切な暗号化メカニズムであるかどうかがわからないためです。

PersistKeysToDbContext

EntityFramework を使ってデータベースにキーを格納するには、Microsoft.AspNetCore.DataProtection.EntityFrameworkCore パッケージを使ってシステムを構成します。

builder.Services.AddDataProtection()
    .PersistKeysToDbContext<SampleDbContext>();

上のコードでは、構成したデータベースにキーが格納されます。 使われているデータベース コンテキストでは、IDataProtectionKeyContext を実装する必要があります。 IDataProtectionKeyContext ではプロパティ DataProtectionKeys が公開されます

public DbSet<DataProtectionKey> DataProtectionKeys { get; set; } = null!;

このプロパティは、キーが格納されるテーブルを表します。 手動で、または DbContext の移行を使ってテーブルを作成します。 詳細については、DataProtectionKeyを参照してください。

ProtectKeysWith*

ProtectKeysWith* 構成 API のいずれかを呼び出すことで、restのキーを保護するようにシステムを構成することができます。 UNC 共有にキーを格納し、 restのキーを特定の X.509 証明書を使って暗号化する、次の例を考えてみます。

builder.Services.AddDataProtection()
    .PersistKeysToFileSystem(new DirectoryInfo(@"\\server\share\directory\"))
    .ProtectKeysWithCertificate(builder.Configuration["CertificateThumbprint"]);

ファイルから読み込んだ証明書など、X509Certificate2ProtectKeysWithCertificate に指定することができます。

builder.Services.AddDataProtection()
    .PersistKeysToFileSystem(new DirectoryInfo(@"\\server\share\directory\"))
    .ProtectKeysWithCertificate(
        new X509Certificate2("certificate.pfx", builder.Configuration["CertificatePassword"]));

組み込みのキー暗号化メカニズムに関するその他の例と説明については、保存時のキーの暗号化Restに関する記事をご覧ください。

UnprotectKeysWithAnyCertificate

UnprotectKeysWithAnyCertificateX509Certificate2 証明書の配列を使って、証明書のローテーションと rest のキーの暗号化解除を行うことができます。

builder.Services.AddDataProtection()
    .PersistKeysToFileSystem(new DirectoryInfo(@"\\server\share\directory\"))
    .ProtectKeysWithCertificate(
        new X509Certificate2("certificate.pfx", builder.Configuration["CertificatePassword"]))
    .UnprotectKeysWithAnyCertificate(
        new X509Certificate2("certificate_1.pfx", builder.Configuration["CertificatePassword_1"]),
        new X509Certificate2("certificate_2.pfx", builder.Configuration["CertificatePassword_2"]));

SetDefaultKeyLifetime

キー有効期間が既定の 90 日ではなく 14 日になるようにシステムを構成するには、SetDefaultKeyLifetime を使います。

builder.Services.AddDataProtection()
    .SetDefaultKeyLifetime(TimeSpan.FromDays(14));

SetApplicationName

既定では、データ保護システムによって各アプリが、それらのコンテンツ ルート パスに基づいて互いに分離されます。同じ物理キー リポジトリを共有している場合でも同様です。 この分離により、各アプリで互いの保護されたペイロードを把握できなくなります。

保護されたペイロードをアプリ間で共有するには:

  • 各アプリで同じ値を使って SetApplicationName を構成します。
  • アプリ全体で同じバージョンのデータ保護 API スタックを使います。 各アプリのプロジェクト ファイルで、次のいずれかを実行します。
builder.Services.AddDataProtection()
    .SetApplicationName("<sharedApplicationName>");

SetApplicationName では内部的に DataProtectionOptions.ApplicationDiscriminator が設定されます。 トラブルシューティングの目的で、フレームワークによって識別子に代入される値は、Program.csWebApplication がビルドされる後に配置された次のコードによってログに記録できます。

var discriminator = app.Services.GetRequiredService<IOptions<DataProtectionOptions>>()
    .Value.ApplicationDiscriminator;
app.Logger.LogInformation("ApplicationDiscriminator: {ApplicationDiscriminator}", discriminator);

識別子の使用方法について詳しくは、この記事の後半にある以下のセクションを参照してください。

警告

.NET 6 では、WebApplicationBuilder によって、コンテンツのルート パスが DirectorySeparatorChar で終わるように正規化されます。 たとえば、Windows では、コンテンツのルート パスは \ で終わります。Linux では / です。 他のホストではパスは正規化されません。 HostBuilder または WebHostBuilder から移行するほとんどのアプリでは DirectorySeparatorChar を終了しないため、同じアプリ名を共有することはありません。 この問題を回避するには、次のコードに示すように、ディレクトリ区切り文字を削除し、アプリ名を手動で設定します。

using Microsoft.AspNetCore.DataProtection;
using System.Reflection;

var builder = WebApplication.CreateBuilder(args);
var trimmedContentRootPath = builder.Environment.ContentRootPath.TrimEnd(Path.DirectorySeparatorChar);
builder.Services.AddDataProtection()
 .SetApplicationName(trimmedContentRootPath);
var app = builder.Build();

app.MapGet("/", () => Assembly.GetEntryAssembly()!.GetName().Name);

app.Run();

DisableAutomaticKeyGeneration

有効期限が近づいたキーをアプリで自動的にロール (新しいキーを作成) したくない場合があります。 このシナリオ例の 1 つは、アプリがプライマリとセカンダリの関係で設定されていて、プライマリ アプリだけがキー管理の問題を担当し、セカンダリ アプリにはキー リングの読み取り専用ビューがあるだけという場合です。 DisableAutomaticKeyGeneration を使ってシステムを構成することで、キー リングを読み取り専用として処理するようにセカンダリ アプリを構成できます。

builder.Services.AddDataProtection()
    .DisableAutomaticKeyGeneration();

アプリケーションごとの分離

ASP.NET Core ホストによってデータ保護システムが提供されている場合、各アプリは自動的に互いに分離されます。これは、それらのアプリが同じワーカー プロセス アカウントで実行され、同じマスター キー マテリアルが使われている場合でも同様です。 これは、System.Web の <machineKey> 要素の IsolateApps 修飾子に似ています。

この分離メカニズムは、ローカル コンピューター上の各アプリを一意のテナントと見なすことで機能するため、特定のアプリに対してルートが指定された IDataProtector には、そのアプリ ID が識別子 (ApplicationDiscriminator) として自動的に含まれます。 アプリの一意の ID は、そのアプリの物理パスです。

  • IIS でホストされているアプリの場合、一意の ID はアプリの IIS 物理パスです。 アプリが Web ファーム環境に展開されている場合、IIS 環境がその Web ファーム内のすべてのコンピューターにわたって同じように構成されていると仮定すれば、この値は変化しません。
  • Kestrel サーバー上で実行しているセルフホステッド アプリの場合、一意の ID はディスク上のアプリへの物理パスです。

一意識別子は、個々のアプリとそのコンピューター自体のどちらをリセットした後でも維持されるように設計されています。

この分離メカニズムは、アプリが悪意のあるものではないことを想定しています。 悪意のあるアプリは、同じワーカー プロセス アカウントで実行されている他のすべてのアプリに常に影響を与える可能性があります。 アプリが相互に信頼されていない共有ホスティング環境の場合、ホスティング プロバイダーによってアプリ間の OS レベルの分離を確保するための手順が実行される必要があります。これにはアプリの基になるキー リポジトリの分離も含まれます。

データ保護システムが ASP.NET Core ホストによって提供されていない場合 (DataProtectionProvider 具象型を使ってそれをインスタンス化する場合など)、アプリの分離は既定で無効になっています。 アプリの分離が無効になっている場合、同じキー マテリアルでサポートされているすべてのアプリで、適切な目的を提供している限り、ペイロードを共有できます。 この環境でアプリの分離を実現するには、構成オブジェクトで SetApplicationName メソッドを呼び出し、アプリごとに一意の名前を指定します。

データ保護とアプリの分離

アプリの分離については、次の点を考慮してください。

  • 複数のアプリが同じキー リポジトリを指している場合は、それらのアプリで同じマスター キー マテリアルを共有することが目的です。 データ保護は、キー リングを共有しているすべてのアプリがそのキー リング内のすべての項目にアクセスできるという前提で開発されています。 アプリケーションの一意識別子は、キー リングによって提供されるキーから派生したアプリケーション固有のキーを分離するために使われます。 項目レベルのアクセス許可は想定されていません (追加の分離を適用するために使われる、Azure KeyVault によって提供されるアクセス許可など)。 項目レベルのアクセス許可を試行すると、アプリケーション エラーが生成されます。 組み込みのアプリケーション分離に依存したくない場合は、別々のキー ストアの場所を使う必要があり、アプリケーション間で共有すべきではありません。

  • アプリケーション識別子 (ApplicationDiscriminator) は、異なるアプリで同じマスター キー マテリアルを共有しながら、それらの暗号化ペイロードを互いに区別できるようにするために使われます。 各アプリで互いの暗号化ペイロードを読み取れるようにするには、それらのアプリケーション識別子が同じである必要があります。それは SetApplicationName を呼び出すことで設定できます。

  • アプリが (たとえば RCE 攻撃によって)侵害された場合、そのアプリからアクセスできるすべてのマスター キー マテリアルも、そのrestの保護の状態に関係なく、侵害されたと見なす必要があります。 これは、2 つのアプリが同じリポジトリを指している場合、それらで異なるアプリ識別子が使われていても、片方の侵害が機能的には両方の侵害に相当することを意味します。

    この「機能的には両方の侵害に相当する」という箇所は、2 つのアプリがrestのキーの保護に異なるメカニズムを使っている場合にも当てはまります。 通常、これは想定される構成ではありません。 restの保護メカニズムは、攻撃者がリポジトリへの読み取りアクセス権を取得した場合に保護を提供することを目的としています。 (おそらくアプリ内でコード実行権限を取得したため) リポジトリへの書き込みアクセス権を取得した攻撃者は、悪意のあるキーをストレージに挿入できます。 データ保護システムでは、キー リポジトリへの書き込みアクセス権を取得した攻撃者に対する保護は意図的に提供されていません。

  • 各アプリを互いに完全に分離した状態に維持する必要がある場合は、異なるキー リポジトリを使う必要があります。 これは当然、"分離" の定義から外れています。 すべてのアプリが互いのデータ ストアに対する読み取りアクセス権と書き込みアクセス権を持っている場合、アプリは分離されて "いません"。

UseCryptographicAlgorithms を使ったアルゴリズムの変更

データ保護スタックを使うと、新しく生成されたキーによって使われる既定のアルゴリズムを変更することができます。 これを行う最も簡単な方法は、構成コールバックから UseCryptographicAlgorithms を呼び出すことです。

builder.Services.AddDataProtection()
    .UseCryptographicAlgorithms(new AuthenticatedEncryptorConfiguration
    {
        EncryptionAlgorithm = EncryptionAlgorithm.AES_256_CBC,
        ValidationAlgorithm = ValidationAlgorithm.HMACSHA256
    });

既定の EncryptionAlgorithm は AES-256-CBC で、既定の ValidationAlgorithm は HMACSHA256 です。 既定のポリシーは、システム管理者がコンピューター全体のポリシーを使って設定できますが、UseCryptographicAlgorithms を明示的に呼び出すと既定のポリシーはオーバーライドされます。

UseCryptographicAlgorithms の呼び出しを使うと、定義済みの組み込みリストから目的のアルゴリズムを指定できます。 アルゴリズムの実装について心配する必要はありません。 上記のシナリオの場合、データ保護システムでは、Windows 上で実行されている場合、AES の CNG 実装の使用が試みられます。 それ以外の場合は、マネージド System.Security.Cryptography.Aes クラスにフォールバックします。

UseCustomCryptographicAlgorithms を呼び出すことで、手動で実装を指定できます。

ヒント

アルゴリズムを変更しても、キー リング内の既存のキーには影響しません。 新たに生成されたキーにのみ影響します。

カスタム マネージド アルゴリズムの指定

カスタム マネージド アルゴリズムを指定するには、実装の種類を示す ManagedAuthenticatedEncryptorConfiguration インスタンスを作成します。

builder.Services.AddDataProtection()
    .UseCustomCryptographicAlgorithms(new ManagedAuthenticatedEncryptorConfiguration
    {
        // A type that subclasses SymmetricAlgorithm
        EncryptionAlgorithmType = typeof(Aes),

        // Specified in bits
        EncryptionAlgorithmKeySize = 256,

        // A type that subclasses KeyedHashAlgorithm
        ValidationAlgorithmType = typeof(HMACSHA256)
    });

一般に、*Type プロパティでは、SymmetricAlgorithmKeyedHashAlgorithm の具体的で、インスタンス化可能 (パブリックのパラメーターなしの ctor を使用) な実装を指す必要がありますが、システムでは、便宜上、typeof(Aes) などのいくつかの値が特別なケースとして扱われます。

Note

SymmetricAlgorithm のキーの長さは 128 ビット以上、ブロック サイズは 64 ビット以上である必要があります。また、PKCS #7 パディングによる CBC モードの暗号化がサポートされている必要があります。 KeyedHashAlgorithm は、ダイジェスト サイズが >=128 ビットである必要があり、ハッシュ アルゴリズムのダイジェストの長さと同じ長さのキーをサポートする必要があります。 KeyedHashAlgorithm は、必ずしも HMAC である必要はありません。

カスタム Windows CNG アルゴリズムの指定

CBC モードの暗号化と HMAC 検証を使ってカスタムの Windows CNG アルゴリズムを指定するには、アルゴリズム情報を含む CngCbcAuthenticatedEncryptorConfiguration インスタンスを作成します。

builder.Services.AddDataProtection()
    .UseCustomCryptographicAlgorithms(new CngCbcAuthenticatedEncryptorConfiguration
    {
        // Passed to BCryptOpenAlgorithmProvider
        EncryptionAlgorithm = "AES",
        EncryptionAlgorithmProvider = null,

        // Specified in bits
        EncryptionAlgorithmKeySize = 256,

        // Passed to BCryptOpenAlgorithmProvider
        HashAlgorithm = "SHA256",
        HashAlgorithmProvider = null
    });

Note

対称ブロック暗号アルゴリズムのキーの長さは >= 128 ビット、ブロック サイズは >= 64 ビットである必要があります。また、PKCS #7 パディングによる CBC モードの暗号化をサポートする必要があります。 ハッシュ アルゴリズムのダイジェスト サイズは >= 128 ビットである必要があり、BCRYPT_ALG_HANDLE_HMAC_FLAG フラグを使って開かれることをサポートする必要があります。 *Provider プロパティを null に設定すると、指定したアルゴリズムの既定のプロバイダーを使用できます。 詳細については、BCryptOpenAlgorithmProvider に関するドキュメントを参照してください。

Galois/Counter モードの暗号化と検証を使ってカスタムの Windows CNG アルゴリズムを指定するには、アルゴリズム情報を含む CngGcmAuthenticatedEncryptorConfiguration インスタンスを作成します。

builder.Services.AddDataProtection()
    .UseCustomCryptographicAlgorithms(new CngGcmAuthenticatedEncryptorConfiguration
    {
        // Passed to BCryptOpenAlgorithmProvider
        EncryptionAlgorithm = "AES",
        EncryptionAlgorithmProvider = null,

        // Specified in bits
        EncryptionAlgorithmKeySize = 256
    });

Note

対称ブロック暗号アルゴリズムのキーの長さは >=128 ビット、ブロック サイズはちょうど 128 ビットである必要があります。また、GCM 暗号化をサポートする必要があります。 EncryptionAlgorithmProvider プロパティを null に設定すると、指定したアルゴリズムの既定のプロバイダーを使用できます。 詳細については、BCryptOpenAlgorithmProvider に関するドキュメントを参照してください。

その他のカスタム アルゴリズムの指定

ファーストクラスの API としては公開されていませんが、データ保護システムにはほとんど全種類のアルゴリズムを指定できるようにする拡張性があります。 たとえば、すべてのキーをハードウェア セキュリティ モジュール (HSM) 内で保持し、コアの暗号化および暗号化解除ルーチンのカスタム実装を提供することができます。 詳細については、コア暗号化機能拡張に関するページの IAuthenticatedEncryptor を参照してください。

Docker コンテナーでホストするときのキーの永続化

Docker コンテナーでホストする場合は、次のいずれかの方法でキーを管理する必要があります。

  • 共有ボリュームやホストによってマウントされたボリュームなど、コンテナーの有効期間を超えて永続化される Docker ボリュームのフォルダー。
  • Azure Blob Storage (「ProtectKeysWithAzureKeyVault」セクションを参照) や Redis などの外部プロバイダー。

Redis でのキーの永続化

キーの格納には、Redis データ永続化がサポートされている Redis バージョンのみを使う必要があります。 Azure Blob Storage は永続的であり、キーの格納に使用できます。 詳細については、こちらの GitHub の問題のページを参照してください。

DataProtection のログ記録

DataProtection の Information レベルのログ記録を有効にして、診断の問題に役立てます。 次の appsettings.json ファイルを使用すると、DataProtection API の情報ログが有効になります。

{
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft.AspNetCore": "Warning",
      "Microsoft.AspNetCore.DataProtection": "Information"
    }
  },
  "AllowedHosts": "*"
}

ログ記録に関する詳細については、「.NET Core と ASP.NET Core でのログ記録」を参照してください。

その他のリソース

データ保護システムを初期化すると、運用環境に基づく既定の設定が適用されます。 これらの設定は、1 台のコンピューター上で実行するアプリに適しています。 ただし、場合によっては、開発者が既定の設定を変更する必要があります。

  • アプリが複数のコンピューターに分散されている。
  • コンプライアンス上の理由から。

これらのシナリオに向けて、データ保護システムには豊富な構成 API が用意されています。

警告

構成ファイルと同様に、適切なアクセス許可を使ってデータ保護のキー リングを保護する必要があります。 restにキーを暗号化することもできますが、攻撃者による新しいキーの作成を防ぐことはできません。 その結果、アプリのセキュリティが影響を受けます。 構成ファイルを保護する方法と同様に、データ保護で構成される保存場所へのアクセスは、そのアプリ自体に制限する必要があります。 たとえば、キー リングをディスクに格納する場合は、ファイル システムのアクセス許可を使います。 Web アプリの実行に使う identity だけが、そのディレクトリへの読み取り、書き込み、作成アクセス権を持つようにする必要があります。 Azure Blob Storage を使う場合は、その Web アプリだけが BLOB ストアでの読み取り、書き込み、または新しいエントリの作成を行えるようにする必要があります。

拡張メソッド AddDataProtection から IDataProtectionBuilder が返されます。 IDataProtectionBuilder によって、データ保護オプションを構成するために連結できる各拡張メソッドが公開されます。

この記事で使うデータ保護拡張機能には、次の NuGet パッケージが必要です。

ProtectKeysWithAzureKeyVault

CLI を使って Azure にサインインします。次に例を示します。

az login

Azure Key Vault にキーを格納するには、Startup クラスで ProtectKeysWithAzureKeyVault を使ってシステムを構成します。 blobUriWithSasToken は、キー ファイルを格納する必要がある完全な URI です。 この URI には、SAS トークンがクエリ文字列パラメーターとして含まれている必要があります。

public void ConfigureServices(IServiceCollection services)
{
    services.AddDataProtection()
        .PersistKeysToAzureBlobStorage(new Uri("<blobUriWithSasToken>"))
        .ProtectKeysWithAzureKeyVault(new Uri("<keyIdentifier>"), new DefaultAzureCredential());
}

アプリが KeyVault と通信し、自身を認可するには、Azure.Identity パッケージを追加する必要があります。

キー リングの保存場所を設定します (例: PersistKeysToAzureBlobStorage)。 この場所を設定する必要がある理由は、ProtectKeysWithAzureKeyVault の呼び出しで、キー リングの保存場所を含む自動データ保護設定を無効にする IXmlEncryptor が実装されているためです。 上記の例では、Azure Blob Storage を使ってキー リングを永続化しています。 詳細については、キー ストレージ プロバイダー:「Azure Storage」を参照してください。 PersistKeysToFileSystem を使ってローカルでキー リングを永続化することもできます。

keyIdentifier は、キーの暗号化に使うキー コンテナーのキー識別子です。 たとえば、contosokeyvault 内の dataprotection という名前のキー コンテナーに作成されたキーのキー識別子は https://contosokeyvault.vault.azure.net/keys/dataprotection/ です。 キー コンテナーに対して、[取得][キーの折り返しを解除]、および [キーを折り返す] のアクセス許可を付与してください。

ProtectKeysWithAzureKeyVault では次がオーバーロードされます。

アプリで古い Azure パッケージ (Microsoft.AspNetCore.DataProtection.AzureStorage と Microsoft.AspNetCore.DataProtection.AzureKeyVault) が使用されている場合、参照を "削除" して、Azure.Extensions.AspNetCore.DataProtection.BlobsAzure.Extensions.AspNetCore.DataProtection.Keys にアップグレードすることをお勧めします。 これらのパッケージでは、新しい更新プログラムを提供し、古いパッケージに関するいくつかの主要なセキュリティおよび安定性の問題に対処しています。

services.AddDataProtection()
    //This blob must already exist before the application is run
    .PersistKeysToAzureBlobStorage("<storage account connection string", "<key store container name>", "<key store blob name>")
    //Removing this line below for an initial run will ensure the file is created correctly
    .ProtectKeysWithAzureKeyVault(new Uri("<keyIdentifier>"), new DefaultAzureCredential());

警告

この記事では、接続文字列の使用方法について説明します。 ローカル データベースでは、ユーザーを認証する必要はありませんが、運用環境では、接続文字列認証用のパスワードが含まれる場合があります。 リソース所有者パスワード資格情報 (ROPC) は、運用データベースで回避する必要があるセキュリティ リスクです。 運用アプリでは、使用可能な最も安全な認証フローを使用する必要があります。 テスト環境または運用環境にデプロイされたアプリの認証の詳細については、「 安全な認証フロー」を参照してください。

PersistKeysToFileSystem

%LOCALAPPDATA% の既定の場所ではなく UNC 共有にキーを格納するには、PersistKeysToFileSystem を使ってシステムを構成します。

public void ConfigureServices(IServiceCollection services)
{
    services.AddDataProtection()
        .PersistKeysToFileSystem(new DirectoryInfo(@"\\server\share\directory\"));
}

警告

キーを永続化する場所を変更した場合、システムによってrestにキーが自動的に暗号化されることはなくなります。DPAPI が適切な暗号化メカニズムであるかどうかがわからないためです。

PersistKeysToDbContext

EntityFramework を使ってデータベースにキーを格納するには、Microsoft.AspNetCore.DataProtection.EntityFrameworkCore パッケージを使ってシステムを構成します。

public void ConfigureServices(IServiceCollection services)
{
    services.AddDataProtection()
        .PersistKeysToDbContext<DbContext>()
}

上のコードでは、構成したデータベースにキーが格納されます。 使われているデータベース コンテキストでは、IDataProtectionKeyContext を実装する必要があります。 IDataProtectionKeyContext ではプロパティ DataProtectionKeys が公開されます

public DbSet<DataProtectionKey> DataProtectionKeys { get; set; }

このプロパティは、キーが格納されるテーブルを表します。 手動で、または DbContext の移行を使ってテーブルを作成します。 詳細については、DataProtectionKeyを参照してください。

ProtectKeysWith*

ProtectKeysWith* 構成 API のいずれかを呼び出すことで、restのキーを保護するようにシステムを構成することができます。 UNC 共有にキーを格納し、 restのキーを特定の X.509 証明書を使って暗号化する、次の例を考えてみます。

public void ConfigureServices(IServiceCollection services)
{
    services.AddDataProtection()
        .PersistKeysToFileSystem(new DirectoryInfo(@"\\server\share\directory\"))
        .ProtectKeysWithCertificate(Configuration["Thumbprint"]);
}

ファイルから読み込んだ証明書など、X509Certificate2ProtectKeysWithCertificate に指定することができます。

public void ConfigureServices(IServiceCollection services)
{
    services.AddDataProtection()
        .PersistKeysToFileSystem(new DirectoryInfo(@"\\server\share\directory\"))
        .ProtectKeysWithCertificate(
            new X509Certificate2("certificate.pfx", Configuration["Thumbprint"]));
}

組み込みのキー暗号化メカニズムに関するその他の例と説明については、保存時のキーの暗号化Restに関する記事をご覧ください。

UnprotectKeysWithAnyCertificate

UnprotectKeysWithAnyCertificateX509Certificate2 証明書の配列を使って、証明書のローテーションと rest のキーの暗号化解除を行うことができます。

public void ConfigureServices(IServiceCollection services)
{
    services.AddDataProtection()
        .PersistKeysToFileSystem(new DirectoryInfo(@"\\server\share\directory\"))
        .ProtectKeysWithCertificate(
            new X509Certificate2("certificate.pfx", Configuration["MyPasswordKey"));
        .UnprotectKeysWithAnyCertificate(
            new X509Certificate2("certificate_old_1.pfx", Configuration["MyPasswordKey_1"]),
            new X509Certificate2("certificate_old_2.pfx", Configuration["MyPasswordKey_2"]));
}

SetDefaultKeyLifetime

キー有効期間が既定の 90 日ではなく 14 日になるようにシステムを構成するには、SetDefaultKeyLifetime を使います。

public void ConfigureServices(IServiceCollection services)
{
    services.AddDataProtection()
        .SetDefaultKeyLifetime(TimeSpan.FromDays(14));
}

SetApplicationName

既定では、データ保護システムによって各アプリが、それらのコンテンツ ルート パスに基づいて互いに分離されます。同じ物理キー リポジトリを共有している場合でも同様です。 この分離により、各アプリで互いの保護されたペイロードを把握できなくなります。

保護されたペイロードをアプリ間で共有するには:

  • 各アプリで同じ値を使って SetApplicationName を構成します。
  • アプリ全体で同じバージョンのデータ保護 API スタックを使います。 各アプリのプロジェクト ファイルで、次のいずれかを実行します。
public void ConfigureServices(IServiceCollection services)
{
    services.AddDataProtection()
        .SetApplicationName("shared app name");
}

SetApplicationName では内部的に DataProtectionOptions.ApplicationDiscriminator が設定されます。 識別子の使用方法について詳しくは、この記事の後半にある以下のセクションを参照してください。

DisableAutomaticKeyGeneration

有効期限が近づいたキーをアプリで自動的にロール (新しいキーを作成) したくない場合があります。 このシナリオ例の 1 つは、アプリがプライマリとセカンダリの関係で設定されていて、プライマリ アプリだけがキー管理の問題を担当し、セカンダリ アプリにはキー リングの読み取り専用ビューがあるだけという場合です。 DisableAutomaticKeyGeneration を使ってシステムを構成することで、キー リングを読み取り専用として処理するようにセカンダリ アプリを構成できます。

public void ConfigureServices(IServiceCollection services)
{
    services.AddDataProtection()
        .DisableAutomaticKeyGeneration();
}

アプリケーションごとの分離

ASP.NET Core ホストによってデータ保護システムが提供されている場合、各アプリは自動的に互いに分離されます。これは、それらのアプリが同じワーカー プロセス アカウントで実行され、同じマスター キー マテリアルが使われている場合でも同様です。 これは、System.Web の <machineKey> 要素の IsolateApps 修飾子に似ています。

この分離メカニズムは、ローカル コンピューター上の各アプリを一意のテナントと見なすことで機能するため、特定のアプリに対してルートが指定された IDataProtector には、そのアプリ ID が識別子 (ApplicationDiscriminator) として自動的に含まれます。 アプリの一意の ID は、そのアプリの物理パスです。

  • IIS でホストされているアプリの場合、一意の ID はアプリの IIS 物理パスです。 アプリが Web ファーム環境に展開されている場合、IIS 環境がその Web ファーム内のすべてのコンピューターにわたって同じように構成されていると仮定すれば、この値は変化しません。
  • Kestrel サーバー上で実行しているセルフホステッド アプリの場合、一意の ID はディスク上のアプリへの物理パスです。

一意識別子は、個々のアプリとそのコンピューター自体のどちらをリセットした後でも維持されるように設計されています。

この分離メカニズムは、アプリが悪意のあるものではないことを想定しています。 悪意のあるアプリは、同じワーカー プロセス アカウントで実行されている他のすべてのアプリに常に影響を与える可能性があります。 アプリが相互に信頼されていない共有ホスティング環境の場合、ホスティング プロバイダーによってアプリ間の OS レベルの分離を確保するための手順が実行される必要があります。これにはアプリの基になるキー リポジトリの分離も含まれます。

データ保護システムが ASP.NET Core ホストによって提供されていない場合 (DataProtectionProvider 具象型を使ってそれをインスタンス化する場合など)、アプリの分離は既定で無効になっています。 アプリの分離が無効になっている場合、同じキー マテリアルでサポートされているすべてのアプリで、適切な目的を提供している限り、ペイロードを共有できます。 この環境でアプリの分離を実現するには、構成オブジェクトで SetApplicationName メソッドを呼び出し、アプリごとに一意の名前を指定します。

データ保護とアプリの分離

アプリの分離については、次の点を考慮してください。

  • 複数のアプリが同じキー リポジトリを指している場合は、それらのアプリで同じマスター キー マテリアルを共有することが目的です。 データ保護は、キー リングを共有しているすべてのアプリがそのキー リング内のすべての項目にアクセスできるという前提で開発されています。 アプリケーションの一意識別子は、キー リングによって提供されるキーから派生したアプリケーション固有のキーを分離するために使われます。 項目レベルのアクセス許可は想定されていません (追加の分離を適用するために使われる、Azure KeyVault によって提供されるアクセス許可など)。 項目レベルのアクセス許可を試行すると、アプリケーション エラーが生成されます。 組み込みのアプリケーション分離に依存したくない場合は、別々のキー ストアの場所を使う必要があり、アプリケーション間で共有すべきではありません。

  • アプリケーション識別子 (ApplicationDiscriminator) は、異なるアプリで同じマスター キー マテリアルを共有しながら、それらの暗号化ペイロードを互いに区別できるようにするために使われます。 各アプリで互いの暗号化ペイロードを読み取れるようにするには、それらのアプリケーション識別子が同じである必要があります。それは SetApplicationName を呼び出すことで設定できます。

  • アプリが (たとえば RCE 攻撃によって)侵害された場合、そのアプリからアクセスできるすべてのマスター キー マテリアルも、そのrestの保護の状態に関係なく、侵害されたと見なす必要があります。 これは、2 つのアプリが同じリポジトリを指している場合、それらで異なるアプリ識別子が使われていても、片方の侵害が機能的には両方の侵害に相当することを意味します。

    この「機能的には両方の侵害に相当する」という箇所は、2 つのアプリがrestのキーの保護に異なるメカニズムを使っている場合にも当てはまります。 通常、これは想定される構成ではありません。 restの保護メカニズムは、攻撃者がリポジトリへの読み取りアクセス権を取得した場合に保護を提供することを目的としています。 (おそらくアプリ内でコード実行権限を取得したため) リポジトリへの書き込みアクセス権を取得した攻撃者は、悪意のあるキーをストレージに挿入できます。 データ保護システムでは、キー リポジトリへの書き込みアクセス権を取得した攻撃者に対する保護は意図的に提供されていません。

  • 各アプリを互いに完全に分離した状態に維持する必要がある場合は、異なるキー リポジトリを使う必要があります。 これは当然、"分離" の定義から外れています。 すべてのアプリが互いのデータ ストアに対する読み取りアクセス権と書き込みアクセス権を持っている場合、アプリは分離されて "いません"。

UseCryptographicAlgorithms を使ったアルゴリズムの変更

データ保護スタックを使うと、新しく生成されたキーによって使われる既定のアルゴリズムを変更することができます。 これを行う最も簡単な方法は、構成コールバックから UseCryptographicAlgorithms を呼び出すことです。

services.AddDataProtection()
    .UseCryptographicAlgorithms(
        new AuthenticatedEncryptorConfiguration()
    {
        EncryptionAlgorithm = EncryptionAlgorithm.AES_256_CBC,
        ValidationAlgorithm = ValidationAlgorithm.HMACSHA256
    });

既定の EncryptionAlgorithm は AES-256-CBC で、既定の ValidationAlgorithm は HMACSHA256 です。 既定のポリシーは、システム管理者がコンピューター全体のポリシーを使って設定できますが、UseCryptographicAlgorithms を明示的に呼び出すと既定のポリシーはオーバーライドされます。

UseCryptographicAlgorithms の呼び出しを使うと、定義済みの組み込みリストから目的のアルゴリズムを指定できます。 アルゴリズムの実装について心配する必要はありません。 上記のシナリオの場合、データ保護システムでは、Windows 上で実行されている場合、AES の CNG 実装の使用が試みられます。 それ以外の場合は、マネージド System.Security.Cryptography.Aes クラスにフォールバックします。

UseCustomCryptographicAlgorithms を呼び出すことで、手動で実装を指定できます。

ヒント

アルゴリズムを変更しても、キー リング内の既存のキーには影響しません。 新たに生成されたキーにのみ影響します。

カスタム マネージド アルゴリズムの指定

カスタム マネージド アルゴリズムを指定するには、実装の種類を示す ManagedAuthenticatedEncryptorConfiguration インスタンスを作成します。

serviceCollection.AddDataProtection()
    .UseCustomCryptographicAlgorithms(
        new ManagedAuthenticatedEncryptorConfiguration()
    {
        // A type that subclasses SymmetricAlgorithm
        EncryptionAlgorithmType = typeof(Aes),

        // Specified in bits
        EncryptionAlgorithmKeySize = 256,

        // A type that subclasses KeyedHashAlgorithm
        ValidationAlgorithmType = typeof(HMACSHA256)
    });

一般に、*Type プロパティでは、SymmetricAlgorithmKeyedHashAlgorithm の具体的で、インスタンス化可能 (パブリックのパラメーターなしの ctor を使用) な実装を指す必要がありますが、システムでは、便宜上、typeof(Aes) などのいくつかの値が特別なケースとして扱われます。

Note

SymmetricAlgorithm のキーの長さは 128 ビット以上、ブロック サイズは 64 ビット以上である必要があります。また、PKCS #7 パディングによる CBC モードの暗号化がサポートされている必要があります。 KeyedHashAlgorithm は、ダイジェスト サイズが >=128 ビットである必要があり、ハッシュ アルゴリズムのダイジェストの長さと同じ長さのキーをサポートする必要があります。 KeyedHashAlgorithm は、必ずしも HMAC である必要はありません。

カスタム Windows CNG アルゴリズムの指定

CBC モードの暗号化と HMAC 検証を使ってカスタムの Windows CNG アルゴリズムを指定するには、アルゴリズム情報を含む CngCbcAuthenticatedEncryptorConfiguration インスタンスを作成します。

services.AddDataProtection()
    .UseCustomCryptographicAlgorithms(
        new CngCbcAuthenticatedEncryptorConfiguration()
    {
        // Passed to BCryptOpenAlgorithmProvider
        EncryptionAlgorithm = "AES",
        EncryptionAlgorithmProvider = null,

        // Specified in bits
        EncryptionAlgorithmKeySize = 256,

        // Passed to BCryptOpenAlgorithmProvider
        HashAlgorithm = "SHA256",
        HashAlgorithmProvider = null
    });

Note

対称ブロック暗号アルゴリズムのキーの長さは >= 128 ビット、ブロック サイズは >= 64 ビットである必要があります。また、PKCS #7 パディングによる CBC モードの暗号化をサポートする必要があります。 ハッシュ アルゴリズムのダイジェスト サイズは >= 128 ビットである必要があり、BCRYPT_ALG_HANDLE_HMAC_FLAG フラグを使って開かれることをサポートする必要があります。 *Provider プロパティを null に設定すると、指定したアルゴリズムの既定のプロバイダーを使用できます。 詳細については、BCryptOpenAlgorithmProvider に関するドキュメントを参照してください。

Galois/Counter モードの暗号化と検証を使ってカスタムの Windows CNG アルゴリズムを指定するには、アルゴリズム情報を含む CngGcmAuthenticatedEncryptorConfiguration インスタンスを作成します。

services.AddDataProtection()
    .UseCustomCryptographicAlgorithms(
        new CngGcmAuthenticatedEncryptorConfiguration()
    {
        // Passed to BCryptOpenAlgorithmProvider
        EncryptionAlgorithm = "AES",
        EncryptionAlgorithmProvider = null,

        // Specified in bits
        EncryptionAlgorithmKeySize = 256
    });

Note

対称ブロック暗号アルゴリズムのキーの長さは >=128 ビット、ブロック サイズはちょうど 128 ビットである必要があります。また、GCM 暗号化をサポートする必要があります。 EncryptionAlgorithmProvider プロパティを null に設定すると、指定したアルゴリズムの既定のプロバイダーを使用できます。 詳細については、BCryptOpenAlgorithmProvider に関するドキュメントを参照してください。

その他のカスタム アルゴリズムの指定

ファーストクラスの API としては公開されていませんが、データ保護システムにはほとんど全種類のアルゴリズムを指定できるようにする拡張性があります。 たとえば、すべてのキーをハードウェア セキュリティ モジュール (HSM) 内で保持し、コアの暗号化および暗号化解除ルーチンのカスタム実装を提供することができます。 詳細については、コア暗号化機能拡張に関するページの IAuthenticatedEncryptor を参照してください。

Docker コンテナーでホストするときのキーの永続化

Docker コンテナーでホストする場合は、次のいずれかの方法でキーを管理する必要があります。

  • 共有ボリュームやホストによってマウントされたボリュームなど、コンテナーの有効期間を超えて永続化される Docker ボリュームのフォルダー。
  • Azure Blob Storage (「ProtectKeysWithAzureKeyVault」セクションを参照) や Redis などの外部プロバイダー。

Redis でのキーの永続化

キーの格納には、Redis データ永続化がサポートされている Redis バージョンのみを使う必要があります。 Azure Blob Storage は永続的であり、キーの格納に使用できます。 詳細については、こちらの GitHub の問題のページを参照してください。

DataProtection のログ記録

DataProtection の Information レベルのログ記録を有効にして、診断の問題に役立てます。 次の appsettings.json ファイルを使用すると、DataProtection API の情報ログが有効になります。

{
  "Logging": {
    "LogLevel": {
      "Microsoft.AspNetCore.DataProtection": "Information"
    }
  }
}

ログ記録に関する詳細については、「.NET Core と ASP.NET Core でのログ記録」を参照してください。

その他のリソース