Отмена защиты полезных данных, ключи которых были отменены в ASP.NET Core
API-интерфейсы защиты основных данных ASP.NET не предназначены в первую очередь для неограниченного сохранения конфиденциальных полезных данных. Другие технологии, такие как Windows CNG DPAPI и Azure Rights Management , более подходят для сценария неограниченного хранилища, и они имеют соответствующие сильные возможности управления ключами. Тем не более чем разработчик не запрещает использовать API защиты данных ASP.NET Core для долгосрочной защиты конфиденциальных данных. Ключи никогда не удаляются из кольца ключей, поэтому IDataProtector.Unprotect
всегда можно восстановить существующие полезные данные, пока ключи доступны и допустимы.
Однако при попытке разработчика отменить защиту данных, защищенных с помощью отозванного ключа, возникает проблема, так как IDataProtector.Unprotect
в этом случае возникает исключение. Это может быть хорошо для коротких или временных полезных данных (таких как маркеры проверки подлинности), так как эти полезные данные могут быть легко воссоздан системой, и в худшем случае посетитель сайта может потребоваться войти снова. Но для сохраняемых полезных данных, Unprotect
что может привести к неприемлемой потере данных.
IPersistedDataProtector
Чтобы обеспечить поддержку сценария, позволяющего незащищенным полезным данным даже перед лицом отозванных ключей, система защиты данных содержит IPersistedDataProtector
тип. Чтобы получить экземпляр, просто получите экземпляр IPersistedDataProtector
в обычном режиме и попробуйте приведение к IDataProtector
IPersistedDataProtector
.IDataProtector
Примечание.
Не все IDataProtector
экземпляры можно привести к IPersistedDataProtector
. Разработчики должны использовать C# как оператор или аналогичные, чтобы избежать исключений среды выполнения, вызванных недопустимыми приведениями, и они должны быть готовы обрабатывать случай сбоя соответствующим образом.
IPersistedDataProtector
предоставляет следующую поверхность API:
DangerousUnprotect(byte[] protectedData, bool ignoreRevocationErrors,
out bool requiresMigration, out bool wasRevoked) : byte[]
Этот API принимает защищенные полезные данные (как массив байтов) и возвращает незащищенные полезные данные. Перегрузка на основе строк отсутствует. Ниже приведены два параметра.
requiresMigration
: задано значениеtrue
, если ключ, используемый для защиты полезных данных, больше не является активным ключом по умолчанию. Например, ключ, используемый для защиты полезных данных, стар, и с тех пор произошла операция скользящего ключа. Вызывающий может рассмотреть возможность повторного защиты полезных данных в зависимости от потребностей бизнеса.wasRevoked
: задано значениеtrue
, если ключ, используемый для защиты полезных данных, был отозван.
Предупреждение
Соблюдайте крайнюю осторожность при передаче ignoreRevocationErrors: true
в DangerousUnprotect
метод. Если после вызова этого метода wasRevoked
значение имеет значение true, то ключ, используемый для защиты этой полезных данных, был отозван, а подлинность полезных данных должна рассматриваться как подозреваемая. В этом случае продолжайте работать только с незащищенными полезными данными, если у вас есть некоторая отдельная уверенность в том, что она является аутентичной, например, из безопасной базы данных, а не отправляется ненадежным веб-клиентом.
using System;
using System.IO;
using System.Text;
using Microsoft.AspNetCore.DataProtection;
using Microsoft.AspNetCore.DataProtection.KeyManagement;
using Microsoft.Extensions.DependencyInjection;
public class Program
{
public static void Main(string[] args)
{
var serviceCollection = new ServiceCollection();
serviceCollection.AddDataProtection()
// point at a specific folder and use DPAPI to encrypt keys
.PersistKeysToFileSystem(new DirectoryInfo(@"c:\temp-keys"))
.ProtectKeysWithDpapi();
var services = serviceCollection.BuildServiceProvider();
// get a protector and perform a protect operation
var protector = services.GetDataProtector("Sample.DangerousUnprotect");
Console.Write("Input: ");
byte[] input = Encoding.UTF8.GetBytes(Console.ReadLine());
var protectedData = protector.Protect(input);
Console.WriteLine($"Protected payload: {Convert.ToBase64String(protectedData)}");
// demonstrate that the payload round-trips properly
var roundTripped = protector.Unprotect(protectedData);
Console.WriteLine($"Round-tripped payload: {Encoding.UTF8.GetString(roundTripped)}");
// get a reference to the key manager and revoke all keys in the key ring
var keyManager = services.GetService<IKeyManager>();
Console.WriteLine("Revoking all keys in the key ring...");
keyManager.RevokeAllKeys(DateTimeOffset.Now, "Sample revocation.");
// try calling Protect - this should throw
Console.WriteLine("Calling Unprotect...");
try
{
var unprotectedPayload = protector.Unprotect(protectedData);
Console.WriteLine($"Unprotected payload: {Encoding.UTF8.GetString(unprotectedPayload)}");
}
catch (Exception ex)
{
Console.WriteLine($"{ex.GetType().Name}: {ex.Message}");
}
// try calling DangerousUnprotect
Console.WriteLine("Calling DangerousUnprotect...");
try
{
IPersistedDataProtector persistedProtector = protector as IPersistedDataProtector;
if (persistedProtector == null)
{
throw new Exception("Can't call DangerousUnprotect.");
}
bool requiresMigration, wasRevoked;
var unprotectedPayload = persistedProtector.DangerousUnprotect(
protectedData: protectedData,
ignoreRevocationErrors: true,
requiresMigration: out requiresMigration,
wasRevoked: out wasRevoked);
Console.WriteLine($"Unprotected payload: {Encoding.UTF8.GetString(unprotectedPayload)}");
Console.WriteLine($"Requires migration = {requiresMigration}, was revoked = {wasRevoked}");
}
catch (Exception ex)
{
Console.WriteLine($"{ex.GetType().Name}: {ex.Message}");
}
}
}
/*
* SAMPLE OUTPUT
*
* Input: Hello!
* Protected payload: CfDJ8LHIzUCX1ZVBn2BZ...
* Round-tripped payload: Hello!
* Revoking all keys in the key ring...
* Calling Unprotect...
* CryptographicException: The key {...} has been revoked.
* Calling DangerousUnprotect...
* Unprotected payload: Hello!
* Requires migration = True, was revoked = True
*/
ASP.NET Core