Поделиться через


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

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

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

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

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

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

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

Примечание.

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

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

Защита ключей с помощью Azure Key Vault

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

az login

Чтобы управлять ключами с помощью Azure Key Vault, настройте систему с помощью ProtectKeysWithAzureKeyVault в Program.cs. 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 Storage. Вы также можете сохранить кольцо ключей локально с помощью PersistKeysToFileSystem.

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

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

Если приложение использует старые пакеты Azure (Microsoft.AspNetCore.DataProtection.AzureStorage и 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 соответствующим механизмом шифрования.

Персистенция ключей в DbContext

Чтобы сохранить ключи в базе данных с помощью 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"]);

Вы можете предоставить X509Certificate2 для ProtectKeysWithCertificate, например, сертификат, загруженный из файла:

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

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

Не защищать ключи с любым сертификатом

Вы можете обновить сертификаты и расшифровать хранящиеся ключи с помощью массива 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"]));

Установить срок действия ключа по умолчанию

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

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

УстановитьИмяПриложения

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

Для обмена защищенными полезными данными между приложениями:

  • Настройте 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.

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

По умолчанию, алгоритм шифрования — AES-256-CBC, а алгоритм проверки — 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 без параметров), реализации SymmetricAlgorithm и KeyedHashAlgorithm, хотя система делает исключения для некоторых значений, таких как typeof(Aes), для удобства.

Примечание.

Симметричный алгоритм должен иметь длину ключа не менее 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.

Ведение журнала защиты данных

Включите 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:

ЗащитаКлючейСИспользованиемAzureKeyVault

Войдите в 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 Storage. Вы также можете сохранить кольцо ключей локально с помощью PersistKeysToFileSystem.

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

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

Если приложение использует старые пакеты Azure (Microsoft.AspNetCore.DataProtection.AzureStorage и 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());

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

Использование строк подключения показано в этой статье. С локальной базой данных пользователю не нужно проходить проверку подлинности, но в производственной среде строка подключения иногда включает пароль для аутентификации. Учетные данные владельца ресурса (ROPC) — это риск безопасности, который следует избежать в рабочих базах данных. Рабочие приложения должны использовать самый безопасный поток проверки подлинности. Дополнительные сведения о проверке подлинности для приложений, развернутых в тестовых или рабочих средах, см. в разделе "Безопасные потоки проверки подлинности".

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

Вы можете предоставить X509Certificate2 для ProtectKeysWithCertificate, например, сертификат, загруженный из файла.

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

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

Снять защиту ключей с любым сертификатом

Вы можете обновить сертификаты и расшифровать ключи в состоянии покоя с помощью массива 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"]));
}

УстановитьСрокДействияКлючаПоУмолчанию

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

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

УстановитьИмяПриложения

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

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

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

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

ОтключитьАвтоматическуюГенерациюКлючей

Возможно, у вас есть сценарий, когда приложение не должно автоматически обновлять ключи (создавать новые ключи), по мере их истечения срока действия. Одним из примеров этого сценария может быть приложение, настроенное в первично-вторичной связи, где только основное приложение отвечает за вопросы управления ключами, а вторичные приложения имеют доступ только для чтения хранилища ключей. Вторичные приложения можно настроить для обработки кольца ключей в режиме только для чтения, настроив систему с помощью 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)
    });

Как правило, свойства типа должны указывать на конкретные, экземплярируемые (через публичные конструкторы без параметров) реализации SymmetricAlgorithm и KeyedHashAlgorithm, хотя для удобства система обрабатывает некоторые значения, такие как typeof(Aes), как специальные случаи.

Примечание.

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

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

Чтобы указать настраиваемый алгоритм Windows CNG с помощью шифрования в режиме 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. Хранилище блобов Azure является постоянным и может использоваться для хранения ключей. Дополнительные сведения см. здесь на GitHub.

Ведение журнала защиты данных

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

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

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

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