ASP.NET Core'da anahtarları iptal edilen yüklerin korumasını kaldırma

ASP.NET Core veri koruma API'leri öncelikli olarak gizli yüklerin süresiz kalıcılığına yönelik değildir. Windows CNG DPAPI ve Azure Rights Management gibi diğer teknolojiler, süresiz depolama senaryosuna daha uygundur ve buna karşılık güçlü anahtar yönetimi özelliklerine sahiptir. Buna göre, bir geliştiricinin gizli verilerin uzun süreli korunması için ASP.NET Core veri koruma API'lerini kullanmasını yasaklayan hiçbir şey yoktur. Anahtarlar hiçbir zaman anahtar halkasından kaldırılmaz, bu nedenle IDataProtector.Unprotect anahtarlar kullanılabilir ve geçerli olduğu sürece mevcut yükleri her zaman kurtarabilir.

Ancak geliştirici, iptal edilmiş bir anahtarla korunan verilerin korumasını kaldırmaya çalıştığında ve bu durumda bir özel durum oluşturacağı için IDataProtector.Unprotect bir sorun ortaya çıkar. Bu tür yükler sistem tarafından kolayca yeniden oluşturulabileceğinden ve en kötü durumda site ziyaretçisinin yeniden oturum açması gerekebileceğinden, kısa süreli veya geçici yüklerde (kimlik doğrulama belirteçleri gibi) bu sorun olmayabilir. Ancak kalıcı yüklerin atılmış olması Unprotect kabul edilemez veri kaybına yol açabilir.

IPersistedDataProtector

İptal edilen anahtarlar karşısında bile yüklerin korumasız olmasına izin verme senaryolarını desteklemek için veri koruma sistemi bir IPersistedDataProtector tür içerir. örneğini IPersistedDataProtectoralmak için örneğini normal bir şekilde almanız IDataProtector ve öğesini olarak atamayı denemeniz IDataProtector yeterlidir IPersistedDataProtector.

Not

Tüm IDataProtector örnekler'e IPersistedDataProtectoryayınlanamaz. Geliştiriciler geçersiz atamaların neden olduğu çalışma zamanı özel durumlarını önlemek için C# işlecini veya benzerini kullanmalıdır ve hata durumunu uygun şekilde işlemeye hazır olmalıdır.

IPersistedDataProtector aşağıdaki API yüzeyini kullanıma sunar:

DangerousUnprotect(byte[] protectedData, bool ignoreRevocationErrors,
     out bool requiresMigration, out bool wasRevoked) : byte[]

Bu API korumalı yükü (bayt dizisi olarak) alır ve korumasız yükü döndürür. Dize tabanlı aşırı yükleme yoktur. İki out parametresi aşağıdaki gibidir.

  • requiresMigration: Bu yükü korumak için kullanılan anahtar artık etkin varsayılan anahtar değilse olarak ayarlanır true . Örneğin, bu yükü korumak için kullanılan anahtar eskidir ve anahtar döndürme işlemi o zamandan beri gerçekleştirilir. Arayan, iş gereksinimlerine bağlı olarak yükü yeniden korumayı düşünebilir.

  • wasRevoked: Bu yükü korumak için kullanılan anahtarın iptal edilmiş olup olmadığını olarak ayarlanır true .

Uyarı

yöntemine DangerousUnprotect geçerken ignoreRevocationErrors: true çok dikkatli olun. Bu yöntem wasRevoked çağrıldıktan sonra değer true ise, bu yükü korumak için kullanılan anahtar iptal edilir ve yükün orijinalliği şüpheli olarak ele alınmalıdır. Bu durumda, korumasız yük üzerinde çalışmaya devam edin; örneğin, güvenilir olmayan bir web istemcisi tarafından gönderilmek yerine güvenli bir veritabanından geldiği gibi, bunun doğru olduğuna dair ayrı bir güvenceniz varsa.

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
 */