Настройка защиты данных в ASP.NET Core

При инициализации системы защиты данных она применяет параметры по умолчанию в зависимости от операционной среды. Эти параметры подходят для приложений, работающих на одном компьютере. Однако есть случаи, когда разработчику может потребоваться изменить параметры по умолчанию:

  • Приложение распространяется на несколько компьютеров.
  • По соображениям соответствия требованиям.

В этих сценариях система защиты данных предлагает широкий API конфигурации.

Предупреждение

Как и в файлах конфигурации, кольцо ключей защиты данных должно быть защищено с помощью соответствующих разрешений. Вы можете зашифровать неактивные ключи, но это не мешает злоумышленникам создавать новые ключи. Следовательно, это влияет на безопасность вашего приложения. Расположение хранилища, настроенного с помощью Data Protection, должно иметь доступ к самому приложению, аналогично тому, как вы будете защищать файлы конфигурации. Например, если вы решили сохранить кольцо ключей на диске, используйте разрешения файловой системы. Убедитесь, что только удостоверение, в котором выполняется веб-приложение, имеет доступ к этому каталогу считывания, записи и создания доступа к этому каталогу. При использовании Хранилище BLOB-объектов Azure только веб-приложение должно иметь возможность читать, записывать или создавать новые записи в хранилище BLOB-объектов и т. д.

Метод AddDataProtection расширения возвращает IDataProtectionBuilderзначение . IDataProtectionBuilder предоставляет методы расширения, которые можно объединить для настройки параметров защиты данных.

Для расширений защиты данных, используемых в этой статье, требуются следующие пакеты NuGet:

ProtectKeysWithAzureKeyVault

Войдите в 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 необходимо добавить пакет AzureIdentity .

Задайте расположение хранилища кольца ключей (например, PersistKeysToAzureBlobStorage). Необходимо задать расположение, так как вызов ProtectKeysWithAzureKeyVault реализует параметры автоматической IXmlEncryptor защиты данных, включая расположение хранилища кольца ключей. В предыдущем примере используется Хранилище BLOB-объектов Azure для сохранения кольца ключей. Дополнительные сведения см. в разделе поставщиков хранилища ключей: служба хранилища Azure. Вы также можете сохранить кольцо ключей локально с помощью PersistKeysToFileSystem.

Это keyIdentifier идентификатор ключа хранилища ключей, используемый для шифрования ключей. Например, ключ, созданный в хранилище ключей с именем dataprotection в contosokeyvault идентификаторе ключа https://contosokeyvault.vault.azure.net/keys/dataprotection/. Предоставьте приложению разрешения Get, Unwrap Key и Wrap Key в хранилище ключей.

ProtectKeysWithAzureKeyVault Перегрузки:

Если приложение использует старые пакеты Azure (Microsoft.AspNetCore.DataProtection.Azure служба хранилища и Microsoft.AspNetCore.DataProtection.AzureKeyVault), рекомендуется удалить эти ссылки и обновить их до Azure.Extensions.AspNetCore.DataProtection.Blobs и Azure.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

Чтобы хранить ключи в UNC-ресурсе вместо расположения %LOCALAPPDATA% по умолчанию, настройте систему с помощью PersistKeysToFileSystem:

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

Предупреждение

Если изменить расположение сохраняемости ключа, система больше не шифрует неактивные ключи, так как он не знает, является ли 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*

Вы можете настроить систему для защиты неактивных ключей, вызвав любой из API конфигурации ProtectKeysWith* . Рассмотрим приведенный ниже пример, в котором хранятся ключи в UNC-ресурсе и шифруются эти ключи с определенным сертификатом 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"]));

Дополнительные примеры и обсуждение встроенных механизмов шифрования ключей см. в разделе "Шифрование неактивных ключей".

UnprotectKeysWithAnyCertificate

Вы можете повернуть сертификаты и расшифровать неактивных ключей с помощью массива X509Certificate2 сертификатов с 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

Чтобы настроить систему для использования времени существования ключа в течение 14 дней вместо 90 дней по умолчанию, используйте SetDefaultKeyLifetimeследующую команду:

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

SetApplicationName

По умолчанию система защиты данных изолирует приложения друг от друга на основе корневых путей содержимого, даже если они совместно используют один и тот же репозиторий физических ключей. Эта изоляция запрещает приложениям понимать защищенные полезные данные друг друга.

Для совместного использования защищенных полезных данных между приложениями:

  • Настройте SetApplicationName в каждом приложении одинаковое значение.
  • Используйте ту же версию стека API защиты данных в приложениях. Выполните одно из следующих действий в файлах проектов приложений:
    • Ссылка на ту же общую версию платформы с помощью метапакета Microsoft.AspNetCore.App.
    • Ссылка на ту же версию пакета защиты данных.
builder.Services.AddDataProtection()
    .SetApplicationName("<sharedApplicationName>");

SetApplicationName внутренние наборы DataProtectionOptions.ApplicationDiscriminator. В целях устранения неполадок значение, назначенное дискриминационным платформой, можно регистрироваться со следующим кодом, помещенным после WebApplication сборки Program.cs:

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

Возможно, у вас есть сценарий, в котором приложение не хочет автоматически свернуть ключи (создать новые ключи) по мере истечения срока действия. Одним из примеров этого сценария может быть приложение, настроенное в первичной или вторичной связи, где только основное приложение отвечает за проблемы управления ключами, а вторичные приложения просто имеют представление только для чтения кольца ключей. Вторичные приложения можно настроить для обработки кольца ключей только для чтения, настроив систему с помощью DisableAutomaticKeyGeneration:

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

Изоляция для каждого приложения

Если система защиты данных предоставляется узлом ASP.NET Core, она автоматически изолирует приложения друг от друга, даже если эти приложения выполняются под одной учетной записью рабочего процесса и используют один и тот же главный материал ключей. Это аналогично модификатору IsolateApps из элемента System.Web <machineKey> .

Механизм изоляции работает путем рассмотрения каждого приложения на локальном компьютере в качестве уникального клиента, таким образом IDataProtector , корневой каталог для любого конкретного приложения автоматически включает идентификатор приложения в качестве дискриминационных (ApplicationDiscriminator). Уникальный идентификатор приложения — это физический путь приложения:

  • Для приложений, размещенных в IIS, уникальный идентификатор — это физический путь к приложению IIS. Если приложение развертывается в среде веб-фермы, это значение стабильно предполагает, что среды IIS настроены одинаково на всех компьютерах в веб-ферме.
  • Для локальных приложений, работающих на сервереKestrel, уникальный идентификатор — это физический путь к приложению на диске.

Уникальный идентификатор предназначен для выживания сбросов как отдельного приложения, так и самого компьютера.

Этот механизм изоляции предполагает, что приложения не являются вредоносными. Вредоносное приложение всегда может повлиять на любое другое приложение, работающее в той же учетной записи рабочего процесса. В общей среде размещения, в которой приложения являются взаимонадежными, поставщик размещения должен предпринять шаги, чтобы обеспечить изоляцию на уровне ОС между приложениями, включая разделение базовых репозиториев ключей приложений.

Если система защиты данных не предоставляется узлом ASP.NET Core (например, если создать экземпляр с помощью конкретного типа) изоляция приложений DataProtectionProvider отключена по умолчанию. Если изоляция приложений отключена, все приложения, поддерживаемые одним и тем же материалом ключей, могут совместно использовать полезные данные, если они предоставляют соответствующие цели. Чтобы обеспечить изоляцию приложений в этой среде, вызовите SetApplicationName метод в объекте конфигурации и укажите уникальное имя для каждого приложения.

Защита данных и изоляция приложений

Рассмотрим следующие моменты для изоляции приложений:

  • При указании нескольких приложений в одном репозитории ключей намерение заключается в том, что приложения используют один и тот же главный материал ключа. Защита данных разрабатывается с предположением, что все приложения, совместно использующие кольцо ключей, могут получить доступ ко всем элементам в этом круге ключей. Уникальный идентификатор приложения используется для изоляции определенных ключей приложения, производных от предоставленных ключей. Он не ожидает, что разрешения на уровне элементов, такие как предоставленные Azure KeyVault, будут использоваться для принудительной дополнительной изоляции. При попытке разрешения уровня элементов возникают ошибки приложения. Если вы не хотите полагаться на встроенную изоляцию приложений, следует использовать отдельные расположения хранилища ключей и не предоставлять общий доступ между приложениями.

  • Дискриминатор приложения (ApplicationDiscriminator) используется для предоставления разным приложениям общего доступа к одному и тому же материалу главного ключа, но для сохранения их криптографических полезных данных, отличных друг от друга. Чтобы приложения могли читать криптографические полезные данные друг друга, они должны иметь одинаковые дискриминационные приложения, которые можно задать путем вызова SetApplicationName.

  • Если приложение скомпрометировано (например, атакой RCE), все основные материалы ключей, доступные для этого приложения, также должны рассматриваться как скомпрометированные независимо от состояния защиты. Это означает, что если два приложения указываются на один репозиторий, даже если они используют разные дискриминационные приложения, компрометация одного из них функционально эквивалентна компромиссу обоих.

    Это "функционально эквивалентно компромиссу обоих" предложений, даже если два приложения используют разные механизмы защиты ключей неактивных данных. Как правило, это не ожидаемая конфигурация. Механизм защиты неактивных данных предназначен для обеспечения защиты в случае, если злоумышленник получает доступ на чтение к репозиторию. Злоумышленник, который получает доступ на запись к репозиторию (возможно, из-за достижения разрешения на выполнение кода в приложении) может вставить вредоносные ключи в хранилище. Система защиты данных намеренно не обеспечивает защиту от злоумышленника, который получает доступ к записи в репозиторий ключей.

  • Если приложения должны оставаться действительно изолированными друг от друга, они должны использовать разные репозитории ключей. Это естественно выходит из определения "изолированных". Приложения не изолированы, если у всех них есть доступ на чтение и запись к хранилищам данных друг друга.

Изменение алгоритмов с помощью UseCryptographicAlgorithms

Стек защиты данных позволяет изменить алгоритм по умолчанию, используемый только что созданными ключами. Самый простой способ сделать это — вызвать из обратного вызова UseCryptographicAlgorithms конфигурации:

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

Значение по умолчанию EncryptionAlgorithm — AES-256-CBC, а значение по умолчанию ValidationAlgorithm — HMACSHA256. Политика по умолчанию может быть задана системным администратором с помощью политики на уровне компьютера, но явный вызов UseCryptographicAlgorithms для переопределения политики по умолчанию.

Вызов UseCryptographicAlgorithms позволяет указать требуемый алгоритм из предопределенного встроенного списка. Вам не нужно беспокоиться о реализации алгоритма. В приведенном выше сценарии система защиты данных пытается использовать реализацию AES CNG при запуске в Windows. В противном случае он возвращается в управляемый 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 должны указывать на конкретные, экземплярируемые (через открытые методы ctor без параметров) реализации иKeyedHashAlgorithm, хотя системные специальные варианты SymmetricAlgorithm некоторые значения, как typeof(Aes) и для удобства.

Примечание.

СимметричныйAlgorithm должен иметь длину ключа ≥ 128 бит и размер блока ≥ 64 бита, и он должен поддерживать шифрование в режиме CBC с помощью PKCS #7. KeyedHashAlgorithm должен иметь размер >дайджеста = 128 бит, и он должен поддерживать ключи длины, равной длине хэш-алгоритма. KeyedHashAlgorithm не является строго обязательным для HMAC.

Указание пользовательских алгоритмов Windows CNG

Чтобы указать пользовательский алгоритм CNG Windows с помощью шифрования в режиме CBC с проверкой HMAC, создайте 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
    });

Примечание.

Алгоритм шифра симметричного блока должен иметь длину >ключа = 128 бит, размер >блока = 64 бита, и он должен поддерживать шифрование в режиме CBC с помощью PKCS #7. Хэш-алгоритм должен иметь размер >дайджеста = 128 бит и должен поддерживать открытие с помощью флага BCRYPT_ALG_HANDLE_HMAC_FLAG. Свойства *Provider можно задать значение NULL, чтобы использовать поставщик по умолчанию для указанного алгоритма. Дополнительные сведения см. в документации BCryptOpenAlgorithmProvider .

Чтобы указать пользовательский алгоритм CNG Windows с помощью шифрования режима Galois/Counter с проверкой, создайте CngGcmAuthenticatedEncryptorConfiguration экземпляр, содержащий алгоритмическую информацию:

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

        // Specified in bits
        EncryptionAlgorithmKeySize = 256
    });

Примечание.

Алгоритм шифра симметричного блока должен иметь длину >ключа = 128 бит, размер блока ровно 128 бит, и он должен поддерживать шифрование GCM. Свойство можно задать EncryptionAlgorithmProvider значение NULL, чтобы использовать поставщик по умолчанию для указанного алгоритма. Дополнительные сведения см. в документации BCryptOpenAlgorithmProvider .

Указание других пользовательских алгоритмов

Хотя и не предоставляется в качестве API первого класса, система защиты данных достаточно расширяема, чтобы разрешить указание почти любого типа алгоритма. Например, можно сохранить все ключи, содержащиеся в аппаратном модуле безопасности (HSM), и предоставить настраиваемую реализацию основных процедур шифрования и расшифровки. Дополнительные сведения см. в статье IAuthenticatedEncryptor о расширяемости криптографии Core.

Сохранение ключей при размещении в контейнере Docker

При размещении в контейнере Docker ключи должны храниться в любом из следующих элементов:

  • Папка, которая является томом Docker, который сохраняется за пределами времени существования контейнера, например общий том или том, подключенный к узлу.
  • Внешний поставщик, например Хранилище BLOB-объектов Azure (показан в ProtectKeysWithAzureKeyVault разделе) или Redis.

Сохранение ключей с помощью Redis

Для хранения ключей следует использовать только версии Redis, поддерживающие сохраняемость данных Redis. Хранилище BLOB-объектов Azure является постоянным и может использоваться для хранения ключей. Дополнительные сведения см. здесь на GitHub.

Ведение журнала DataProtection

Включите Information ведение журнала уровня DataProtection, чтобы помочь диагностировать проблему. appsettings.json Следующий файл включает ведение журнала сведений API DataProtection:

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

Дополнительные сведения о ведении журнала см. в разделе "Ведение журнала" в .NET Core и ASP.NET Core.

Дополнительные ресурсы

При инициализации системы защиты данных она применяет параметры по умолчанию в зависимости от операционной среды. Эти параметры подходят для приложений, работающих на одном компьютере. Однако есть случаи, когда разработчику может потребоваться изменить параметры по умолчанию:

  • Приложение распространяется на несколько компьютеров.
  • По соображениям соответствия требованиям.

В этих сценариях система защиты данных предлагает широкий API конфигурации.

Предупреждение

Как и в файлах конфигурации, кольцо ключей защиты данных должно быть защищено с помощью соответствующих разрешений. Вы можете зашифровать неактивные ключи, но это не мешает злоумышленникам создавать новые ключи. Следовательно, это влияет на безопасность вашего приложения. Расположение хранилища, настроенного с помощью Data Protection, должно иметь доступ к самому приложению, аналогично тому, как вы будете защищать файлы конфигурации. Например, если вы решили сохранить кольцо ключей на диске, используйте разрешения файловой системы. Убедитесь, что только удостоверение, в котором выполняется веб-приложение, имеет доступ к этому каталогу считывания, записи и создания доступа к этому каталогу. При использовании Хранилище BLOB-объектов Azure только веб-приложение должно иметь возможность читать, записывать или создавать новые записи в хранилище BLOB-объектов и т. д.

Метод AddDataProtection расширения возвращает IDataProtectionBuilderзначение . IDataProtectionBuilder предоставляет методы расширения, которые можно объединить для настройки параметров защиты данных.

Для расширений защиты данных, используемых в этой статье, требуются следующие пакеты NuGet:

ProtectKeysWithAzureKeyVault

Войдите в Azure с помощью интерфейса командной строки, например:

az login

Чтобы сохранить ключи в Azure Key Vault, настройте систему в ProtectKeysWithAzureKeyVaultStartup классе. blobUriWithSasToken — полный универсальный код ресурса (URI), в котором должен храниться файл ключа. Универсальный код ресурса (URI) должен содержать маркер SAS в качестве параметра строки запроса:

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

Для взаимодействия и авторизации приложения с помощью KeyVault необходимо добавить пакет AzureIdentity .

Задайте расположение хранилища кольца ключей (например, PersistKeysToAzureBlobStorage). Необходимо задать расположение, так как вызов ProtectKeysWithAzureKeyVault реализует параметры автоматической IXmlEncryptor защиты данных, включая расположение хранилища кольца ключей. В предыдущем примере используется Хранилище BLOB-объектов Azure для сохранения кольца ключей. Дополнительные сведения см. в разделе поставщиков хранилища ключей: служба хранилища Azure. Вы также можете сохранить кольцо ключей локально с помощью PersistKeysToFileSystem.

Это keyIdentifier идентификатор ключа хранилища ключей, используемый для шифрования ключей. Например, ключ, созданный в хранилище ключей с именем dataprotection в contosokeyvault идентификаторе ключа https://contosokeyvault.vault.azure.net/keys/dataprotection/. Предоставьте приложению разрешения Get, Unwrap Key и Wrap Key в хранилище ключей.

ProtectKeysWithAzureKeyVault Перегрузки:

Если приложение использует старые пакеты Azure (Microsoft.AspNetCore.DataProtection.Azure служба хранилища и Microsoft.AspNetCore.DataProtection.AzureKeyVault), рекомендуется удалить эти ссылки и обновить их до Azure.Extensions.AspNetCore.DataProtection.Blobs и Azure.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());

PersistKeysToFileSystem

Чтобы хранить ключи в UNC-ресурсе вместо расположения %LOCALAPPDATA% по умолчанию, настройте систему с помощью PersistKeysToFileSystem:

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

Предупреждение

Если изменить расположение сохраняемости ключа, система больше не шифрует неактивные ключи, так как он не знает, является ли 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*

Вы можете настроить систему для защиты неактивных ключей, вызвав любой из API конфигурации ProtectKeysWith* . Рассмотрим приведенный ниже пример, в котором хранятся ключи в UNC-ресурсе и шифруются эти ключи с определенным сертификатом 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"]));
}

Дополнительные примеры и обсуждение встроенных механизмов шифрования ключей см. в разделе "Шифрование неактивных ключей".

UnprotectKeysWithAnyCertificate

Вы можете повернуть сертификаты и расшифровать неактивных ключей с помощью массива X509Certificate2 сертификатов с 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

Чтобы настроить систему для использования времени существования ключа в течение 14 дней вместо 90 дней по умолчанию, используйте SetDefaultKeyLifetimeследующую команду:

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

SetApplicationName

По умолчанию система защиты данных изолирует приложения друг от друга на основе корневых путей содержимого, даже если они совместно используют один и тот же репозиторий физических ключей. Эта изоляция запрещает приложениям понимать защищенные полезные данные друг друга.

Для совместного использования защищенных полезных данных между приложениями:

  • Настройте SetApplicationName в каждом приложении одинаковое значение.
  • Используйте ту же версию стека API защиты данных в приложениях. Выполните одно из следующих действий в файлах проектов приложений:
    • Ссылка на ту же общую версию платформы с помощью метапакета Microsoft.AspNetCore.App.
    • Ссылка на ту же версию пакета защиты данных.
public void ConfigureServices(IServiceCollection services)
{
    services.AddDataProtection()
        .SetApplicationName("shared app name");
}

SetApplicationName внутренние наборы DataProtectionOptions.ApplicationDiscriminator. Дополнительные сведения о том, как используется дискриминация, см. в следующих разделах ниже в этой статье:

DisableAutomaticKeyGeneration

Возможно, у вас есть сценарий, в котором приложение не хочет автоматически свернуть ключи (создать новые ключи) по мере истечения срока действия. Одним из примеров этого сценария может быть приложение, настроенное в первичной или вторичной связи, где только основное приложение отвечает за проблемы управления ключами, а вторичные приложения просто имеют представление только для чтения кольца ключей. Вторичные приложения можно настроить для обработки кольца ключей только для чтения, настроив систему с помощью DisableAutomaticKeyGeneration:

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

Изоляция для каждого приложения

Если система защиты данных предоставляется узлом ASP.NET Core, она автоматически изолирует приложения друг от друга, даже если эти приложения выполняются под одной учетной записью рабочего процесса и используют один и тот же главный материал ключей. Это аналогично модификатору IsolateApps из элемента System.Web <machineKey> .

Механизм изоляции работает путем рассмотрения каждого приложения на локальном компьютере в качестве уникального клиента, таким образом IDataProtector , корневой каталог для любого конкретного приложения автоматически включает идентификатор приложения в качестве дискриминационных (ApplicationDiscriminator). Уникальный идентификатор приложения — это физический путь приложения:

  • Для приложений, размещенных в IIS, уникальный идентификатор — это физический путь к приложению IIS. Если приложение развертывается в среде веб-фермы, это значение стабильно предполагает, что среды IIS настроены одинаково на всех компьютерах в веб-ферме.
  • Для локальных приложений, работающих на сервереKestrel, уникальный идентификатор — это физический путь к приложению на диске.

Уникальный идентификатор предназначен для выживания сбросов как отдельного приложения, так и самого компьютера.

Этот механизм изоляции предполагает, что приложения не являются вредоносными. Вредоносное приложение всегда может повлиять на любое другое приложение, работающее в той же учетной записи рабочего процесса. В общей среде размещения, в которой приложения являются взаимонадежными, поставщик размещения должен предпринять шаги, чтобы обеспечить изоляцию на уровне ОС между приложениями, включая разделение базовых репозиториев ключей приложений.

Если система защиты данных не предоставляется узлом ASP.NET Core (например, если создать экземпляр с помощью конкретного типа) изоляция приложений DataProtectionProvider отключена по умолчанию. Если изоляция приложений отключена, все приложения, поддерживаемые одним и тем же материалом ключей, могут совместно использовать полезные данные, если они предоставляют соответствующие цели. Чтобы обеспечить изоляцию приложений в этой среде, вызовите метод SetApplicationName в объекте конфигурации и укажите уникальное имя для каждого приложения.

Защита данных и изоляция приложений

Рассмотрим следующие моменты для изоляции приложений:

  • При указании нескольких приложений в одном репозитории ключей намерение заключается в том, что приложения используют один и тот же главный материал ключа. Защита данных разрабатывается с предположением, что все приложения, совместно использующие кольцо ключей, могут получить доступ ко всем элементам в этом круге ключей. Уникальный идентификатор приложения используется для изоляции определенных ключей приложения, производных от предоставленных ключей. Он не ожидает, что разрешения на уровне элементов, такие как предоставленные Azure KeyVault, будут использоваться для принудительной дополнительной изоляции. При попытке разрешения уровня элементов возникают ошибки приложения. Если вы не хотите полагаться на встроенную изоляцию приложений, следует использовать отдельные расположения хранилища ключей и не предоставлять общий доступ между приложениями.

  • Дискриминатор приложения (ApplicationDiscriminator) используется для предоставления разным приложениям общего доступа к одному и тому же материалу главного ключа, но для сохранения их криптографических полезных данных, отличных друг от друга. Чтобы приложения могли читать криптографические полезные данные друг друга, они должны иметь одинаковые дискриминационные приложения, которые можно задать путем вызова SetApplicationName.

  • Если приложение скомпрометировано (например, атакой RCE), все основные материалы ключей, доступные для этого приложения, также должны рассматриваться как скомпрометированные независимо от состояния защиты. Это означает, что если два приложения указываются на один репозиторий, даже если они используют разные дискриминационные приложения, компрометация одного из них функционально эквивалентна компромиссу обоих.

    Это "функционально эквивалентно компромиссу обоих" предложений, даже если два приложения используют разные механизмы защиты ключей неактивных данных. Как правило, это не ожидаемая конфигурация. Механизм защиты неактивных данных предназначен для обеспечения защиты в случае, если злоумышленник получает доступ на чтение к репозиторию. Злоумышленник, который получает доступ на запись к репозиторию (возможно, из-за достижения разрешения на выполнение кода в приложении) может вставить вредоносные ключи в хранилище. Система защиты данных намеренно не обеспечивает защиту от злоумышленника, который получает доступ к записи в репозиторий ключей.

  • Если приложения должны оставаться действительно изолированными друг от друга, они должны использовать разные репозитории ключей. Это естественно выходит из определения "изолированных". Приложения не изолированы, если у всех них есть доступ на чтение и запись к хранилищам данных друг друга.

Изменение алгоритмов с помощью UseCryptographicAlgorithms

Стек защиты данных позволяет изменить алгоритм по умолчанию, используемый только что созданными ключами. Самый простой способ сделать это — вызвать из обратного вызова UseCryptographicAlgorithms конфигурации:

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

Значение по умолчанию EncryptionAlgorithm — AES-256-CBC, а значение по умолчанию ValidationAlgorithm — HMACSHA256. Политика по умолчанию может быть задана системным администратором с помощью политики на уровне компьютера, но явный вызов UseCryptographicAlgorithms для переопределения политики по умолчанию.

Вызов UseCryptographicAlgorithms позволяет указать требуемый алгоритм из предопределенного встроенного списка. Вам не нужно беспокоиться о реализации алгоритма. В приведенном выше сценарии система защиты данных пытается использовать реализацию AES CNG при запуске в Windows. В противном случае он возвращается в управляемый 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 должны указывать на конкретные, экземплярируемые (через открытые методы ctor без параметров) реализации иKeyedHashAlgorithm, хотя системные специальные варианты SymmetricAlgorithm некоторые значения, как typeof(Aes) и для удобства.

Примечание.

СимметричныйAlgorithm должен иметь длину ключа ≥ 128 бит и размер блока ≥ 64 бита, и он должен поддерживать шифрование в режиме CBC с помощью PKCS #7. KeyedHashAlgorithm должен иметь размер >дайджеста = 128 бит, и он должен поддерживать ключи длины, равной длине хэш-алгоритма. KeyedHashAlgorithm не является строго обязательным для HMAC.

Указание пользовательских алгоритмов Windows CNG

Чтобы указать пользовательский алгоритм CNG Windows с помощью шифрования в режиме CBC с проверкой HMAC, создайте 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
    });

Примечание.

Алгоритм шифра симметричного блока должен иметь длину >ключа = 128 бит, размер >блока = 64 бита, и он должен поддерживать шифрование в режиме CBC с помощью PKCS #7. Хэш-алгоритм должен иметь размер >дайджеста = 128 бит и должен поддерживать открытие с помощью флага BCRYPT_ALG_HANDLE_HMAC_FLAG. Свойства *Provider можно задать значение NULL, чтобы использовать поставщик по умолчанию для указанного алгоритма. Дополнительные сведения см. в документации BCryptOpenAlgorithmProvider .

Чтобы указать пользовательский алгоритм CNG Windows с помощью шифрования режима Galois/Counter с проверкой, создайте CngGcmAuthenticatedEncryptorConfiguration экземпляр, содержащий алгоритмическую информацию:

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

        // Specified in bits
        EncryptionAlgorithmKeySize = 256
    });

Примечание.

Алгоритм шифра симметричного блока должен иметь длину >ключа = 128 бит, размер блока ровно 128 бит, и он должен поддерживать шифрование GCM. Свойство можно задать EncryptionAlgorithmProvider значение NULL, чтобы использовать поставщик по умолчанию для указанного алгоритма. Дополнительные сведения см. в документации BCryptOpenAlgorithmProvider .

Указание других пользовательских алгоритмов

Хотя и не предоставляется в качестве API первого класса, система защиты данных достаточно расширяема, чтобы разрешить указание почти любого типа алгоритма. Например, можно сохранить все ключи, содержащиеся в аппаратном модуле безопасности (HSM), и предоставить настраиваемую реализацию основных процедур шифрования и расшифровки. Дополнительные сведения см. в статье IAuthenticatedEncryptor о расширяемости криптографии Core.

Сохранение ключей при размещении в контейнере Docker

При размещении в контейнере Docker ключи должны храниться в любом из следующих элементов:

  • Папка, которая является томом Docker, который сохраняется за пределами времени существования контейнера, например общий том или том, подключенный к узлу.
  • Внешний поставщик, например Хранилище BLOB-объектов Azure (показан в ProtectKeysWithAzureKeyVault разделе) или Redis.

Сохранение ключей с помощью Redis

Для хранения ключей следует использовать только версии Redis, поддерживающие сохраняемость данных Redis. Хранилище BLOB-объектов Azure является постоянным и может использоваться для хранения ключей. Дополнительные сведения см. здесь на GitHub.

Ведение журнала DataProtection

Включите Information ведение журнала уровня DataProtection, чтобы помочь диагностировать проблему. appsettings.json Следующий файл включает ведение журнала сведений API DataProtection:

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

Дополнительные сведения о ведении журнала см. в разделе "Ведение журнала" в .NET Core и ASP.NET Core.

Дополнительные ресурсы