Konfigurowanie ochrony danych na platformie ASP.NET Core

Gdy system ochrony danych jest inicjowany, stosuje ustawienia domyślne na podstawie środowiska operacyjnego. Te ustawienia są odpowiednie dla aplikacji działających na jednej maszynie. Istnieją jednak przypadki, w których deweloper może chcieć zmienić ustawienia domyślne:

  • Aplikacja jest rozłożona na wiele maszyn.
  • Ze względów zgodności.

W tych scenariuszach system ochrony danych oferuje zaawansowany interfejs API konfiguracji.

Ostrzeżenie

Podobnie jak w przypadku plików konfiguracji pierścień kluczy ochrony danych powinien być chroniony przy użyciu odpowiednich uprawnień. Możesz wybrać szyfrowanie kluczy magazynowanych, ale nie uniemożliwia to osobie atakującej tworzenia nowych kluczy. W związku z tym ma to wpływ na bezpieczeństwo aplikacji. Lokalizacja magazynu skonfigurowana przy użyciu usługi Data Protection powinna mieć ograniczony dostęp do samej aplikacji, podobnie jak w przypadku ochrony plików konfiguracji. Jeśli na przykład zdecydujesz się przechowywać pierścień kluczy na dysku, użyj uprawnień systemu plików. Upewnij się, że tylko tożsamość, w ramach której działa aplikacja internetowa, ma dostęp do odczytu, zapisu i tworzenia dostępu do tego katalogu. Jeśli używasz usługi Azure Blob Storage, tylko aplikacja internetowa powinna mieć możliwość odczytu, zapisu lub tworzenia nowych wpisów w magazynie obiektów blob itp.

Metoda rozszerzenia zwraca wartość AddDataProtectionIDataProtectionBuilder. IDataProtectionBuilder Uwidacznia metody rozszerzeń, które można połączyć w celu skonfigurowania opcji ochrony danych.

Następujące pakiety NuGet są wymagane dla rozszerzeń ochrony danych używanych w tym artykule:

ProtectKeysWithAzureKeyVault

Zaloguj się do platformy Azure przy użyciu interfejsu wiersza polecenia, na przykład:

az login

Aby zarządzać kluczami za pomocą usługi Azure Key Vault, skonfiguruj system za pomocą ProtectKeysWithAzureKeyVault polecenia w programie Program.cs. blobUriWithSasToken to pełny identyfikator URI, w którym powinien być przechowywany plik klucza. Identyfikator URI musi zawierać token SAS jako parametr ciągu zapytania:

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

Aby aplikacja komunikowała się i autoryzować się za pomocą usługi KeyVault, należy dodać pakiet platformy Azure.Identity

Ustaw lokalizację przechowywania pierścienia kluczy (na przykład PersistKeysToAzureBlobStorage). Lokalizacja musi być ustawiona, ponieważ wywołanie ProtectKeysWithAzureKeyVault implementuje IXmlEncryptor funkcję , która wyłącza ustawienia automatycznej ochrony danych, w tym lokalizację przechowywania pierścienia kluczy. W poprzednim przykładzie użyto usługi Azure Blob Storage do utrwalania pierścienia kluczy. Aby uzyskać więcej informacji, zobacz Dostawcy magazynu kluczy: Azure Storage. Możesz również utrwalać pierścień kluczy lokalnie za pomocą funkcji PersistKeysToFileSystem.

Jest keyIdentifier to identyfikator klucza magazynu kluczy używany do szyfrowania kluczy. Na przykład klucz utworzony w magazynie kluczy o nazwie dataprotection w obiekcie contosokeyvault ma identyfikator https://contosokeyvault.vault.azure.net/keys/dataprotection/klucza . Podaj aplikację z uprawnieniami Pobierz, Odpakuj klucz i Zawij klucz do magazynu kluczy.

ProtectKeysWithAzureKeyVault Przeciążenia:

Jeśli aplikacja korzysta ze starszych pakietów platformy Azure (Microsoft.AspNetCore.DataProtection.AzureStorage i Microsoft.AspNetCore.DataProtection.AzureKeyVault), zalecamy usunięcie tych odwołań i uaktualnienie do plików Azure.Extensions.AspNetCore.DataProtection.Blobs i Azure.Extensions.AspNetCore.DataProtection.Keys. Te pakiety to miejsce, w którym są udostępniane nowe aktualizacje, oraz rozwiązywanie niektórych kluczowych problemów z zabezpieczeniami i stabilnością starszych pakietów.

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

Aby przechowywać klucze w udziale UNC zamiast w domyślnej lokalizacji %LOCALAPPDATA% , skonfiguruj system za pomocą polecenia PersistKeysToFileSystem:

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

Ostrzeżenie

Jeśli zmienisz lokalizację trwałości klucza, system nie będzie już automatycznie szyfrować kluczy magazynowanych, ponieważ nie wie, czy dpAPI jest odpowiednim mechanizmem szyfrowania.

PersistKeysToDbContext

Aby przechowywać klucze w bazie danych przy użyciu elementu EntityFramework, skonfiguruj system przy użyciu pakietu Microsoft.AspNetCore.DataProtection.EntityFrameworkCore :

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

Powyższy kod przechowuje klucze w skonfigurowanej bazie danych. Używany kontekst bazy danych musi implementować IDataProtectionKeyContextelement . IDataProtectionKeyContext uwidacznia właściwość DataProtectionKeys

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

Ta właściwość reprezentuje tabelę, w której są przechowywane klucze. Utwórz tabelę ręcznie lub za pomocą DbContext funkcji Migracje. W celu uzyskania więcej informacji, zobacz następujący temat: DataProtectionKey.

ProtectKeysWith*

System można skonfigurować tak, aby chronił klucze magazynowane, wywołując dowolny z interfejsów API konfiguracji ProtectKeysWith* . Rozważmy poniższy przykład, który przechowuje klucze w udziale UNC i szyfruje klucze magazynowane przy użyciu określonego certyfikatu X.509:

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

Możesz podać element X509Certificate2 do ProtectKeysWithCertificateelementu , taki jak certyfikat załadowany z pliku:

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

Zobacz Szyfrowanie kluczy magazynowane , aby uzyskać więcej przykładów i omówienie wbudowanych mechanizmów szyfrowania kluczy.

UnprotectKeysWithAnyCertificate

Certyfikaty i odszyfrowywanie kluczy magazynowanych można wymieniać przy użyciu tablicy certyfikatów X509Certificate2 z UnprotectKeysWithAnyCertificate:

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

Aby skonfigurować system do używania okresu istnienia klucza 14 dni zamiast domyślnego 90 dni, użyj polecenia SetDefaultKeyLifetime:

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

SetApplicationName

Domyślnie system ochrony danych izoluje aplikacje od siebie na podstawie ścieżek głównych zawartości, nawet jeśli współużytkują to samo repozytorium kluczy fizycznych. Ta izolacja uniemożliwia aplikacjom zrozumienie ładunków chronionych przez siebie.

Aby udostępnić chronione ładunki między aplikacjami:

builder.Services.AddDataProtection()
    .SetApplicationName("<sharedApplicationName>");

SetApplicationName program wewnętrznie ustawia wartość DataProtectionOptions.ApplicationDiscriminator. W celach rozwiązywania problemów wartość przypisana dyskryminującemu przez platformę może być rejestrowana przy użyciu następującego kodu umieszczonego po WebApplication skompilowaniu elementu Program.cs:

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

Aby uzyskać więcej informacji na temat sposobu użycia dyskryminującego, zobacz następujące sekcje w dalszej części tego artykułu:

Ostrzeżenie

Na platformie .NET 6 WebApplicationBuilder normalizuje ścieżkę katalogu głównego zawartości, aby zakończyć ciąg .DirectorySeparatorChar Na przykład w systemie Windows ścieżka główna zawartości kończy się na \ i w systemie Linux /. Inne hosty nie normalizują ścieżki. Większość aplikacji migrujących z HostBuilder programu lub WebHostBuilder nie będzie współdzielić tej samej nazwy aplikacji, ponieważ nie będzie miała zakończenia .DirectorySeparatorChar Aby obejść ten problem, usuń znak separatora katalogu i ręcznie ustaw nazwę aplikacji, jak pokazano w poniższym kodzie:

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

Może istnieć scenariusz, w którym nie chcesz, aby aplikacja automatycznie rzutować klucze (tworzyć nowe klucze) w miarę zbliżania się do wygaśnięcia. Jednym z przykładów tego scenariusza mogą być aplikacje skonfigurowane w relacji podstawowej/pomocniczej, gdzie tylko podstawowa aplikacja jest odpowiedzialna za kluczowe problemy z zarządzaniem, a aplikacje pomocnicze mają po prostu widok pierścienia kluczy tylko do odczytu. Aplikacje pomocnicze można skonfigurować tak, aby traktować pierścień kluczy jako tylko do odczytu, konfigurując system przy użyciu polecenia DisableAutomaticKeyGeneration:

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

Izolacja dla aplikacji

Gdy system ochrony danych jest dostarczany przez hosta ASP.NET Core, automatycznie izoluje aplikacje od siebie, nawet jeśli te aplikacje są uruchomione na tym samym koncie procesu roboczego i używają tego samego głównego materiału kluczy. Jest to podobne do modyfikatora IsolateApps z elementu System.Web <machineKey> .

Mechanizm izolacji działa, biorąc pod uwagę każdą aplikację na komputerze lokalnym jako unikatową dzierżawę, w związku z czym IDataProtector rooted dla danej aplikacji automatycznie uwzględnia identyfikator aplikacji jako dyskryminujący (ApplicationDiscriminator). Unikatowy identyfikator aplikacji to ścieżka fizyczna aplikacji:

  • W przypadku aplikacji hostowanych w usługach IIS unikatowy identyfikator to ścieżka fizyczna usług IIS aplikacji. Jeśli aplikacja jest wdrażana w środowisku farmy sieci Web, ta wartość jest stabilna przy założeniu, że środowiska usług IIS są podobnie skonfigurowane na wszystkich maszynach w farmie sieci Web.
  • W przypadku aplikacji hostowanych samodzielnie na Kestrel serwerze unikatowy identyfikator to ścieżka fizyczna do aplikacji na dysku.

Unikatowy identyfikator jest przeznaczony do przetrwania resetowania — zarówno pojedynczej aplikacji, jak i samej maszyny.

Ten mechanizm izolacji zakłada, że aplikacje nie są złośliwe. Złośliwa aplikacja zawsze może mieć wpływ na dowolną inną aplikację działającą na tym samym koncie procesu roboczego. W udostępnionym środowisku hostingu, w którym aplikacje są wzajemnie niezaufane, dostawca hostingu powinien podjąć kroki w celu zapewnienia izolacji na poziomie systemu operacyjnego między aplikacjami, w tym oddzielenia bazowych repozytoriów kluczy aplikacji.

Jeśli system ochrony danych nie jest udostępniany przez hosta platformy ASP.NET Core (na przykład w przypadku utworzenia wystąpienia za pośrednictwem konkretnego DataProtectionProvider typu) izolacja aplikacji jest domyślnie wyłączona. Gdy izolacja aplikacji jest wyłączona, wszystkie aplikacje wspierane przez ten sam materiał kluczy mogą współdzielić ładunki, o ile zapewniają odpowiednie cele. Aby zapewnić izolację aplikacji w tym środowisku, wywołaj SetApplicationName metodę w obiekcie konfiguracji i podaj unikatową nazwę dla każdej aplikacji.

Ochrona danych i izolacja aplikacji

Rozważ następujące kwestie dotyczące izolacji aplikacji:

  • Gdy wiele aplikacji jest wskazywanych na to samo repozytorium kluczy, intencją jest to, że aplikacje współużytkują ten sam materiał klucza głównego. Ochrona danych jest opracowywana przy założeniu, że wszystkie aplikacje współużytkujące pierścień kluczy mogą uzyskiwać dostęp do wszystkich elementów w tym pierścieniu kluczy. Unikatowy identyfikator aplikacji służy do izolowania kluczy specyficznych dla aplikacji pochodzących z kluczy dostarczonych przez pierścień kluczy. Nie oczekuje uprawnień na poziomie elementu, takich jak te udostępniane przez usługę Azure KeyVault do wymuszania dodatkowej izolacji. Próba uzyskania uprawnień na poziomie elementu powoduje wygenerowanie błędów aplikacji. Jeśli nie chcesz polegać na wbudowanej izolacji aplikacji, należy używać oddzielnych lokalizacji magazynu kluczy i nie udostępniać ich między aplikacjami.

  • Dyskryminujące aplikacje (ApplicationDiscriminator) umożliwiają różnym aplikacjom współużytkowanie tego samego materiału klucza głównego, ale przechowywanie ich ładunków kryptograficznych różni się od siebie. Aby aplikacje mogły odczytywać ze sobą ładunki kryptograficzne, muszą mieć tę samą dyskryminującą aplikację, którą można ustawić przez wywołanie metody SetApplicationName.

  • W przypadku naruszenia zabezpieczeń aplikacji (na przykład przez atak RCE), wszystkie materiały klucza głównego dostępne dla tej aplikacji muszą być również uznawane za naruszone, niezależnie od stanu ochrony w spoczynku. Oznacza to, że jeśli dwa aplikacje są wskazywane na to samo repozytorium, nawet jeśli korzystają z różnych dyskryminujących aplikacji, kompromis jednego z nich jest funkcjonalnie równoważny z naruszeniem obu tych elementów.

    Ta klauzula "funkcjonalnie równoważna naruszeniu obu" jest przechowywana nawet wtedy, gdy obie aplikacje używają różnych mechanizmów ochrony kluczy magazynowanych. Zazwyczaj nie jest to oczekiwana konfiguracja. Mechanizm ochrony w spoczynku ma zapewnić ochronę w przypadku, gdy przeciwnik uzyskuje dostęp do odczytu do repozytorium. Osoba atakująca, która uzyskuje dostęp do zapisu do repozytorium (być może ze względu na to, że uzyskali uprawnienia do wykonywania kodu w aplikacji), może wstawić złośliwe klucze do magazynu. System ochrony danych celowo nie zapewnia ochrony przed przeciwnikiem, który uzyskuje dostęp do zapisu w repozytorium kluczy.

  • Jeśli aplikacje muszą pozostać naprawdę odizolowane od siebie, powinny używać różnych repozytoriów kluczy. To naturalnie wypada z definicji "izolowanej". Aplikacje nieizolowane, jeśli wszystkie mają dostęp do odczytu i zapisu do magazynów danych.

Zmienianie algorytmów przy użyciu metody UseCryptographicAlgorithms

Stos ochrony danych umożliwia zmianę domyślnego algorytmu używanego przez nowo wygenerowane klucze. Najprostszym sposobem wykonania tej czynności jest wywołanie z wywołania UseCryptographicAlgorithms zwrotnego konfiguracji:

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

Wartość domyślna EncryptionAlgorithm to AES-256-CBC, a domyślna wartość ValidationAlgorithm to HMACSHA256. Zasady domyślne mogą być ustawiane przez administratora systemu za pośrednictwem zasad dla całej maszyny, ale jawne wywołanie UseCryptographicAlgorithms zastąpi domyślne zasady.

Wywołanie UseCryptographicAlgorithms umożliwia określenie żądanego algorytmu ze wstępnie zdefiniowanej wbudowanej listy. Nie musisz martwić się o implementację algorytmu. W powyższym scenariuszu system ochrony danych próbuje użyć implementacji CNG AES, jeśli jest uruchomiona w systemie Windows. W przeciwnym razie wraca do klasy zarządzanej System.Security.Cryptography.Aes .

Implementację można określić ręcznie za pomocą wywołania metody UseCustomCryptographicAlgorithms.

Napiwek

Zmiana algorytmów nie ma wpływu na istniejące klucze w pierścieniu kluczy. Dotyczy tylko nowo wygenerowanych kluczy.

Określanie niestandardowych algorytmów zarządzanych

Aby określić niestandardowe algorytmy zarządzane, utwórz ManagedAuthenticatedEncryptorConfiguration wystąpienie wskazujące typy implementacji:

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)
    });

Ogólnie rzecz biorąc właściwości *Type muszą wskazywać konkretne, wystąpienia (za pośrednictwem publicznego ctor bez parametrów) implementacji SymmetricAlgorithm i KeyedHashAlgorithm, choć system specjalne przypadki niektórych wartości, takich jak typeof(Aes) dla wygody.

Uwaga

SymmetricAlgorithm musi mieć długość klucza ≥ 128 bitów i rozmiar bloku ≥ 64 bitów i musi obsługiwać szyfrowanie w trybie CBC za pomocą dopełnienia PKCS #7. KluczedHashAlgorithm musi mieć rozmiar skrótu >= 128 bitów i musi obsługiwać klucze długości równej długości skrótu algorytmu skrótu. KeyedHashAlgorithm nie jest ściśle wymagany do bycia HMAC.

Określanie niestandardowych algorytmów CNG systemu Windows

Aby określić niestandardowy algorytm CNG systemu Windows przy użyciu szyfrowania WBC w trybie HMAC, utwórz CngCbcAuthenticatedEncryptorConfiguration wystąpienie zawierające informacje algorytmiczne:

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
    });

Uwaga

Algorytm szyfrowania bloków symetrycznych musi mieć długość >klucza = 128 bitów, rozmiar >bloku = 64 bity i musi obsługiwać szyfrowanie w trybie CBC za pomocą dopełnienia PKCS #7. Algorytm skrótu musi mieć rozmiar skrótu >= 128 bitów i musi obsługiwać otwieranie za pomocą flagi BCRYPT_ALG_HANDLE_HMAC_FLAG. *Właściwości dostawcy można ustawić na wartość null, aby użyć domyślnego dostawcy dla określonego algorytmu. Aby uzyskać więcej informacji, zobacz dokumentację BCryptOpenAlgorithmProvider .

Aby określić niestandardowy algorytm CNG systemu Windows przy użyciu szyfrowania Galois/Counter Mode z walidacją, utwórz CngGcmAuthenticatedEncryptorConfiguration wystąpienie zawierające informacje algorytmiczne:

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

        // Specified in bits
        EncryptionAlgorithmKeySize = 256
    });

Uwaga

Algorytm szyfrowania bloków symetrycznych musi mieć długość >klucza = 128 bitów, rozmiar bloku o dokładnie 128 bitach i musi obsługiwać szyfrowanie GCM. Właściwość można ustawić EncryptionAlgorithmProvider na wartość null, aby użyć domyślnego dostawcy dla określonego algorytmu. Aby uzyskać więcej informacji, zobacz dokumentację BCryptOpenAlgorithmProvider .

Określanie innych algorytmów niestandardowych

Mimo że system ochrony danych nie jest uwidaczniony jako interfejs API pierwszej klasy, jest wystarczająco rozszerzalny, aby umożliwić określenie niemal dowolnego rodzaju algorytmu. Na przykład można zachować wszystkie klucze zawarte w sprzętowym module zabezpieczeń (HSM) i zapewnić niestandardową implementację procedur szyfrowania i odszyfrowywania rdzeni. Aby uzyskać więcej informacji, zobacz IAuthenticatedEncryptor w temacie Rozszerzalność kryptografii podstawowej.

Utrwalanie kluczy podczas hostowania w kontenerze platformy Docker

W przypadku hostowania w kontenerze platformy Docker klucze powinny być przechowywane w następujących elementach:

  • Folder, który jest woluminem platformy Docker, który utrzymuje się poza okresem istnienia kontenera, na przykład woluminem udostępnionym lub woluminem zainstalowanym na hoście.
  • Dostawca zewnętrzny, taki jak Azure Blob Storage (pokazany ProtectKeysWithAzureKeyVault w sekcji) lub Redis.

Utrwalanie kluczy za pomocą usługi Redis

Do przechowywania kluczy powinny być używane tylko wersje usługi Redis obsługujące trwałość danych Redis. Usługa Azure Blob Storage jest trwała i może służyć do przechowywania kluczy. Aby uzyskać więcej informacji, zobacz ten problem w serwisie GitHub.

Rejestrowanie ochrony danych

Włącz Information rejestrowanie na poziomie funkcji DataProtection, aby pomóc w diagnozowaniu problemu. appsettings.json Następujący plik umożliwia rejestrowanie informacji interfejsu API ochrony danych:

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

Aby uzyskać więcej informacji na temat rejestrowania, zobacz Rejestrowanie na platformie .NET Core i ASP.NET Core.

Dodatkowe zasoby

Gdy system ochrony danych jest inicjowany, stosuje ustawienia domyślne na podstawie środowiska operacyjnego. Te ustawienia są odpowiednie dla aplikacji działających na jednej maszynie. Istnieją jednak przypadki, w których deweloper może chcieć zmienić ustawienia domyślne:

  • Aplikacja jest rozłożona na wiele maszyn.
  • Ze względów zgodności.

W tych scenariuszach system ochrony danych oferuje zaawansowany interfejs API konfiguracji.

Ostrzeżenie

Podobnie jak w przypadku plików konfiguracji pierścień kluczy ochrony danych powinien być chroniony przy użyciu odpowiednich uprawnień. Możesz wybrać szyfrowanie kluczy magazynowanych, ale nie uniemożliwia to osobie atakującej tworzenia nowych kluczy. W związku z tym ma to wpływ na bezpieczeństwo aplikacji. Lokalizacja magazynu skonfigurowana przy użyciu usługi Data Protection powinna mieć ograniczony dostęp do samej aplikacji, podobnie jak w przypadku ochrony plików konfiguracji. Jeśli na przykład zdecydujesz się przechowywać pierścień kluczy na dysku, użyj uprawnień systemu plików. Upewnij się, że tylko tożsamość, w ramach której działa aplikacja internetowa, ma dostęp do odczytu, zapisu i tworzenia dostępu do tego katalogu. Jeśli używasz usługi Azure Blob Storage, tylko aplikacja internetowa powinna mieć możliwość odczytu, zapisu lub tworzenia nowych wpisów w magazynie obiektów blob itp.

Metoda rozszerzenia zwraca wartość AddDataProtectionIDataProtectionBuilder. IDataProtectionBuilder Uwidacznia metody rozszerzeń, które można połączyć w celu skonfigurowania opcji ochrony danych.

Następujące pakiety NuGet są wymagane dla rozszerzeń ochrony danych używanych w tym artykule:

ProtectKeysWithAzureKeyVault

Zaloguj się do platformy Azure przy użyciu interfejsu wiersza polecenia, na przykład:

az login

Aby przechowywać klucze w usłudze Azure Key Vault, skonfiguruj system ProtectKeysWithAzureKeyVault w Startup klasie . blobUriWithSasToken to pełny identyfikator URI, w którym powinien być przechowywany plik klucza. Identyfikator URI musi zawierać token SAS jako parametr ciągu zapytania:

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

Aby aplikacja komunikowała się i autoryzować się za pomocą usługi KeyVault, należy dodać pakiet platformy Azure.Identity

Ustaw lokalizację przechowywania pierścienia kluczy (na przykład PersistKeysToAzureBlobStorage). Lokalizacja musi być ustawiona, ponieważ wywołanie ProtectKeysWithAzureKeyVault implementuje IXmlEncryptor funkcję , która wyłącza ustawienia automatycznej ochrony danych, w tym lokalizację przechowywania pierścienia kluczy. W poprzednim przykładzie użyto usługi Azure Blob Storage do utrwalania pierścienia kluczy. Aby uzyskać więcej informacji, zobacz Dostawcy magazynu kluczy: Azure Storage. Możesz również utrwalać pierścień kluczy lokalnie za pomocą funkcji PersistKeysToFileSystem.

Jest keyIdentifier to identyfikator klucza magazynu kluczy używany do szyfrowania kluczy. Na przykład klucz utworzony w magazynie kluczy o nazwie dataprotection w obiekcie contosokeyvault ma identyfikator https://contosokeyvault.vault.azure.net/keys/dataprotection/klucza . Podaj aplikację z uprawnieniami Pobierz, Odpakuj klucz i Zawij klucz do magazynu kluczy.

ProtectKeysWithAzureKeyVault Przeciążenia:

Jeśli aplikacja korzysta ze starszych pakietów platformy Azure (Microsoft.AspNetCore.DataProtection.AzureStorage i Microsoft.AspNetCore.DataProtection.AzureKeyVault), zalecamy usunięcie tych odwołań i uaktualnienie do plików Azure.Extensions.AspNetCore.DataProtection.Blobs i Azure.Extensions.AspNetCore.DataProtection.Keys. Te pakiety to miejsce, w którym są udostępniane nowe aktualizacje, oraz rozwiązywanie niektórych kluczowych problemów z zabezpieczeniami i stabilnością starszych pakietów.

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());

PersistKeysToFileSystem

Aby przechowywać klucze w udziale UNC zamiast w domyślnej lokalizacji %LOCALAPPDATA% , skonfiguruj system za pomocą polecenia PersistKeysToFileSystem:

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

Ostrzeżenie

Jeśli zmienisz lokalizację trwałości klucza, system nie będzie już automatycznie szyfrować kluczy magazynowanych, ponieważ nie wie, czy dpAPI jest odpowiednim mechanizmem szyfrowania.

PersistKeysToDbContext

Aby przechowywać klucze w bazie danych przy użyciu elementu EntityFramework, skonfiguruj system przy użyciu pakietu Microsoft.AspNetCore.DataProtection.EntityFrameworkCore :

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

Powyższy kod przechowuje klucze w skonfigurowanej bazie danych. Używany kontekst bazy danych musi implementować IDataProtectionKeyContextelement . IDataProtectionKeyContext uwidacznia właściwość DataProtectionKeys

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

Ta właściwość reprezentuje tabelę, w której są przechowywane klucze. Utwórz tabelę ręcznie lub za pomocą DbContext funkcji Migracje. W celu uzyskania więcej informacji, zobacz następujący temat: DataProtectionKey.

ProtectKeysWith*

System można skonfigurować tak, aby chronił klucze magazynowane, wywołując dowolny z interfejsów API konfiguracji ProtectKeysWith* . Rozważmy poniższy przykład, który przechowuje klucze w udziale UNC i szyfruje klucze magazynowane przy użyciu określonego certyfikatu X.509:

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

Możesz podać element X509Certificate2 do ProtectKeysWithCertificateelementu , taki jak certyfikat załadowany z pliku:

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

Zobacz Szyfrowanie kluczy magazynowane , aby uzyskać więcej przykładów i omówienie wbudowanych mechanizmów szyfrowania kluczy.

UnprotectKeysWithAnyCertificate

Certyfikaty i odszyfrowywanie kluczy magazynowanych można wymieniać przy użyciu tablicy certyfikatów X509Certificate2 z UnprotectKeysWithAnyCertificate:

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

Aby skonfigurować system do używania okresu istnienia klucza 14 dni zamiast domyślnego 90 dni, użyj polecenia SetDefaultKeyLifetime:

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

SetApplicationName

Domyślnie system ochrony danych izoluje aplikacje od siebie na podstawie ścieżek głównych zawartości, nawet jeśli współużytkują to samo repozytorium kluczy fizycznych. Ta izolacja uniemożliwia aplikacjom zrozumienie ładunków chronionych przez siebie.

Aby udostępnić chronione ładunki między aplikacjami:

public void ConfigureServices(IServiceCollection services)
{
    services.AddDataProtection()
        .SetApplicationName("shared app name");
}

SetApplicationName program wewnętrznie ustawia wartość DataProtectionOptions.ApplicationDiscriminator. Aby uzyskać więcej informacji na temat sposobu użycia dyskryminującego, zobacz następujące sekcje w dalszej części tego artykułu:

DisableAutomaticKeyGeneration

Może istnieć scenariusz, w którym nie chcesz, aby aplikacja automatycznie rzutować klucze (tworzyć nowe klucze) w miarę zbliżania się do wygaśnięcia. Jednym z przykładów tego scenariusza mogą być aplikacje skonfigurowane w relacji podstawowej/pomocniczej, gdzie tylko podstawowa aplikacja jest odpowiedzialna za kluczowe problemy z zarządzaniem, a aplikacje pomocnicze mają po prostu widok pierścienia kluczy tylko do odczytu. Aplikacje pomocnicze można skonfigurować tak, aby traktować pierścień kluczy jako tylko do odczytu, konfigurując system przy użyciu polecenia DisableAutomaticKeyGeneration:

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

Izolacja dla aplikacji

Gdy system ochrony danych jest dostarczany przez hosta ASP.NET Core, automatycznie izoluje aplikacje od siebie, nawet jeśli te aplikacje są uruchomione na tym samym koncie procesu roboczego i używają tego samego głównego materiału kluczy. Jest to podobne do modyfikatora IsolateApps z elementu System.Web <machineKey> .

Mechanizm izolacji działa, biorąc pod uwagę każdą aplikację na komputerze lokalnym jako unikatową dzierżawę, w związku z czym IDataProtector rooted dla danej aplikacji automatycznie uwzględnia identyfikator aplikacji jako dyskryminujący (ApplicationDiscriminator). Unikatowy identyfikator aplikacji to ścieżka fizyczna aplikacji:

  • W przypadku aplikacji hostowanych w usługach IIS unikatowy identyfikator to ścieżka fizyczna usług IIS aplikacji. Jeśli aplikacja jest wdrażana w środowisku farmy sieci Web, ta wartość jest stabilna przy założeniu, że środowiska usług IIS są podobnie skonfigurowane na wszystkich maszynach w farmie sieci Web.
  • W przypadku aplikacji hostowanych samodzielnie na Kestrel serwerze unikatowy identyfikator to ścieżka fizyczna do aplikacji na dysku.

Unikatowy identyfikator jest przeznaczony do przetrwania resetowania — zarówno pojedynczej aplikacji, jak i samej maszyny.

Ten mechanizm izolacji zakłada, że aplikacje nie są złośliwe. Złośliwa aplikacja zawsze może mieć wpływ na dowolną inną aplikację działającą na tym samym koncie procesu roboczego. W udostępnionym środowisku hostingu, w którym aplikacje są wzajemnie niezaufane, dostawca hostingu powinien podjąć kroki w celu zapewnienia izolacji na poziomie systemu operacyjnego między aplikacjami, w tym oddzielenia bazowych repozytoriów kluczy aplikacji.

Jeśli system ochrony danych nie jest udostępniany przez hosta platformy ASP.NET Core (na przykład w przypadku utworzenia wystąpienia za pośrednictwem konkretnego DataProtectionProvider typu) izolacja aplikacji jest domyślnie wyłączona. Gdy izolacja aplikacji jest wyłączona, wszystkie aplikacje wspierane przez ten sam materiał kluczy mogą współdzielić ładunki, o ile zapewniają odpowiednie cele. Aby zapewnić izolację aplikacji w tym środowisku, wywołaj metodę SetApplicationName w obiekcie konfiguracji i podaj unikatową nazwę dla każdej aplikacji.

Ochrona danych i izolacja aplikacji

Rozważ następujące kwestie dotyczące izolacji aplikacji:

  • Gdy wiele aplikacji jest wskazywanych na to samo repozytorium kluczy, intencją jest to, że aplikacje współużytkują ten sam materiał klucza głównego. Ochrona danych jest opracowywana przy założeniu, że wszystkie aplikacje współużytkujące pierścień kluczy mogą uzyskiwać dostęp do wszystkich elementów w tym pierścieniu kluczy. Unikatowy identyfikator aplikacji służy do izolowania kluczy specyficznych dla aplikacji pochodzących z kluczy dostarczonych przez pierścień kluczy. Nie oczekuje uprawnień na poziomie elementu, takich jak te udostępniane przez usługę Azure KeyVault do wymuszania dodatkowej izolacji. Próba uzyskania uprawnień na poziomie elementu powoduje wygenerowanie błędów aplikacji. Jeśli nie chcesz polegać na wbudowanej izolacji aplikacji, należy używać oddzielnych lokalizacji magazynu kluczy i nie udostępniać ich między aplikacjami.

  • Dyskryminujące aplikacje (ApplicationDiscriminator) umożliwiają różnym aplikacjom współużytkowanie tego samego materiału klucza głównego, ale przechowywanie ich ładunków kryptograficznych różni się od siebie. Aby aplikacje mogły odczytywać ze sobą ładunki kryptograficzne, muszą mieć tę samą dyskryminującą aplikację, którą można ustawić przez wywołanie metody SetApplicationName.

  • W przypadku naruszenia zabezpieczeń aplikacji (na przykład przez atak RCE), wszystkie materiały klucza głównego dostępne dla tej aplikacji muszą być również uznawane za naruszone, niezależnie od stanu ochrony w spoczynku. Oznacza to, że jeśli dwa aplikacje są wskazywane na to samo repozytorium, nawet jeśli korzystają z różnych dyskryminujących aplikacji, kompromis jednego z nich jest funkcjonalnie równoważny z naruszeniem obu tych elementów.

    Ta klauzula "funkcjonalnie równoważna naruszeniu obu" jest przechowywana nawet wtedy, gdy obie aplikacje używają różnych mechanizmów ochrony kluczy magazynowanych. Zazwyczaj nie jest to oczekiwana konfiguracja. Mechanizm ochrony w spoczynku ma zapewnić ochronę w przypadku, gdy przeciwnik uzyskuje dostęp do odczytu do repozytorium. Osoba atakująca, która uzyskuje dostęp do zapisu do repozytorium (być może ze względu na to, że uzyskali uprawnienia do wykonywania kodu w aplikacji), może wstawić złośliwe klucze do magazynu. System ochrony danych celowo nie zapewnia ochrony przed przeciwnikiem, który uzyskuje dostęp do zapisu w repozytorium kluczy.

  • Jeśli aplikacje muszą pozostać naprawdę odizolowane od siebie, powinny używać różnych repozytoriów kluczy. To naturalnie wypada z definicji "izolowanej". Aplikacje nieizolowane, jeśli wszystkie mają dostęp do odczytu i zapisu do magazynów danych.

Zmienianie algorytmów przy użyciu metody UseCryptographicAlgorithms

Stos ochrony danych umożliwia zmianę domyślnego algorytmu używanego przez nowo wygenerowane klucze. Najprostszym sposobem wykonania tej czynności jest wywołanie z wywołania UseCryptographicAlgorithms zwrotnego konfiguracji:

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

Wartość domyślna EncryptionAlgorithm to AES-256-CBC, a domyślna wartość ValidationAlgorithm to HMACSHA256. Zasady domyślne mogą być ustawiane przez administratora systemu za pośrednictwem zasad dla całej maszyny, ale jawne wywołanie UseCryptographicAlgorithms zastąpi domyślne zasady.

Wywołanie UseCryptographicAlgorithms umożliwia określenie żądanego algorytmu ze wstępnie zdefiniowanej wbudowanej listy. Nie musisz martwić się o implementację algorytmu. W powyższym scenariuszu system ochrony danych próbuje użyć implementacji CNG AES, jeśli jest uruchomiona w systemie Windows. W przeciwnym razie wraca do klasy zarządzanej System.Security.Cryptography.Aes .

Implementację można określić ręcznie za pomocą wywołania metody UseCustomCryptographicAlgorithms.

Napiwek

Zmiana algorytmów nie ma wpływu na istniejące klucze w pierścieniu kluczy. Dotyczy tylko nowo wygenerowanych kluczy.

Określanie niestandardowych algorytmów zarządzanych

Aby określić niestandardowe algorytmy zarządzane, utwórz ManagedAuthenticatedEncryptorConfiguration wystąpienie wskazujące typy implementacji:

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)
    });

Ogólnie rzecz biorąc właściwości *Type muszą wskazywać konkretne, wystąpienia (za pośrednictwem publicznego ctor bez parametrów) implementacji SymmetricAlgorithm i KeyedHashAlgorithm, choć system specjalne przypadki niektórych wartości, takich jak typeof(Aes) dla wygody.

Uwaga

SymmetricAlgorithm musi mieć długość klucza ≥ 128 bitów i rozmiar bloku ≥ 64 bitów i musi obsługiwać szyfrowanie w trybie CBC za pomocą dopełnienia PKCS #7. KluczedHashAlgorithm musi mieć rozmiar skrótu >= 128 bitów i musi obsługiwać klucze długości równej długości skrótu algorytmu skrótu. KeyedHashAlgorithm nie jest ściśle wymagany do bycia HMAC.

Określanie niestandardowych algorytmów CNG systemu Windows

Aby określić niestandardowy algorytm CNG systemu Windows przy użyciu szyfrowania WBC w trybie HMAC, utwórz CngCbcAuthenticatedEncryptorConfiguration wystąpienie zawierające informacje algorytmiczne:

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

        // Specified in bits
        EncryptionAlgorithmKeySize = 256,

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

Uwaga

Algorytm szyfrowania bloków symetrycznych musi mieć długość >klucza = 128 bitów, rozmiar >bloku = 64 bity i musi obsługiwać szyfrowanie w trybie CBC za pomocą dopełnienia PKCS #7. Algorytm skrótu musi mieć rozmiar skrótu >= 128 bitów i musi obsługiwać otwieranie za pomocą flagi BCRYPT_ALG_HANDLE_HMAC_FLAG. *Właściwości dostawcy można ustawić na wartość null, aby użyć domyślnego dostawcy dla określonego algorytmu. Aby uzyskać więcej informacji, zobacz dokumentację BCryptOpenAlgorithmProvider .

Aby określić niestandardowy algorytm CNG systemu Windows przy użyciu szyfrowania Galois/Counter Mode z walidacją, utwórz CngGcmAuthenticatedEncryptorConfiguration wystąpienie zawierające informacje algorytmiczne:

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

        // Specified in bits
        EncryptionAlgorithmKeySize = 256
    });

Uwaga

Algorytm szyfrowania bloków symetrycznych musi mieć długość >klucza = 128 bitów, rozmiar bloku o dokładnie 128 bitach i musi obsługiwać szyfrowanie GCM. Właściwość można ustawić EncryptionAlgorithmProvider na wartość null, aby użyć domyślnego dostawcy dla określonego algorytmu. Aby uzyskać więcej informacji, zobacz dokumentację BCryptOpenAlgorithmProvider .

Określanie innych algorytmów niestandardowych

Mimo że system ochrony danych nie jest uwidaczniony jako interfejs API pierwszej klasy, jest wystarczająco rozszerzalny, aby umożliwić określenie niemal dowolnego rodzaju algorytmu. Na przykład można zachować wszystkie klucze zawarte w sprzętowym module zabezpieczeń (HSM) i zapewnić niestandardową implementację procedur szyfrowania i odszyfrowywania rdzeni. Aby uzyskać więcej informacji, zobacz IAuthenticatedEncryptor w temacie Rozszerzalność kryptografii podstawowej.

Utrwalanie kluczy podczas hostowania w kontenerze platformy Docker

W przypadku hostowania w kontenerze platformy Docker klucze powinny być przechowywane w następujących elementach:

  • Folder, który jest woluminem platformy Docker, który utrzymuje się poza okresem istnienia kontenera, na przykład woluminem udostępnionym lub woluminem zainstalowanym na hoście.
  • Dostawca zewnętrzny, taki jak Azure Blob Storage (pokazany ProtectKeysWithAzureKeyVault w sekcji) lub Redis.

Utrwalanie kluczy za pomocą usługi Redis

Do przechowywania kluczy powinny być używane tylko wersje usługi Redis obsługujące trwałość danych Redis. Usługa Azure Blob Storage jest trwała i może służyć do przechowywania kluczy. Aby uzyskać więcej informacji, zobacz ten problem w serwisie GitHub.

Rejestrowanie ochrony danych

Włącz Information rejestrowanie na poziomie funkcji DataProtection, aby pomóc w diagnozowaniu problemu. appsettings.json Następujący plik umożliwia rejestrowanie informacji interfejsu API ochrony danych:

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

Aby uzyskać więcej informacji na temat rejestrowania, zobacz Rejestrowanie na platformie .NET Core i ASP.NET Core.

Dodatkowe zasoby