Visão geral das APIs de consumidor para o ASP.NET Core

As interfaces IDataProtectionProvider e IDataProtector são as interfaces básicas por meio das quais os consumidores usam o sistema de proteção de dados. Elas estão localizadas no pacote Microsoft.AspNetCore.DataProtection.Abstractions.

IDataProtectionProvider

A interface do provedor representa a raiz do sistema de proteção de dados. Ela não pode ser usada diretamente para proteger ou desproteger dados. Em vez disso, o consumidor deve obter uma referência a um IDataProtector chamando IDataProtectionProvider.CreateProtector(purpose), em que finalidade é uma cadeia de caracteres que descreve o caso de uso do consumidor pretendido. Consulte Cadeias de Caracteres de Finalidade para obter muito mais informações sobre a intenção desse parâmetro e como escolher um valor adequado.

IDataProtector

A interface do protetor é retornada por uma chamada para CreateProtector, e é essa interface que os consumidores podem usar para executar operações de proteção e desproteção.

Para proteger uma parte dos dados, passe os dados para o método Protect. A interface básica define um método que converte byte[] -> byte[], mas também há uma sobrecarga (fornecida como um método de extensão) que converte cadeia de caracteres -> cadeia de caracteres. A segurança oferecida pelos dois métodos é idêntica; o desenvolvedor deve escolher a sobrecarga que for mais conveniente para o seu caso de uso. Independentemente da sobrecarga escolhida, o valor retornado pelo método Protect agora está protegido (cifrado e inviolável), e o aplicativo pode enviá-lo a um cliente não confiável.

Para desproteger uma parte de dados protegida anteriormente, passe os dados protegidos para o método Unprotect. (Há sobrecargas baseadas em bytes[]e em cadeia de caracteres para conveniência do desenvolvedor.) Se o conteúdo protegido tiver sido gerado por uma chamada anterior para Protect nesse mesmo IDataProtector, o método Unprotect retornará o conteúdo desprotegido original. Se o conteúdo protegido tiver sido adulterado ou produzido por um diferente IDataProtector, o método Unprotect lançará uma CryptographicException.

O conceito de IDataProtector igual vs. diferente está ligado ao conceito de finalidade. Se duas instâncias IDataProtector foram geradas a partir da mesma raiz IDataProtectionProvider, mas por meio de cadeias de caracteres de finalidade diferentes na chamada para IDataProtectionProvider.CreateProtector, elas serão consideradas protetores diferentes, e uma não poderá desproteger cargas geradas pela outra.

Consumo dessas interfaces

Para um componente com reconhecimento de DI, o uso pretendido é que o componente receba um parâmetro IDataProtectionProvider em seu construtor e que o sistema DI forneça automaticamente esse serviço quando o componente for instanciado.

Observação

Alguns aplicativos (como os aplicativos de console ou os aplicativos ASP.NET 4.x) podem não ter reconhecimento de DI e, portanto, não podem usar o mecanismo descrito aqui. Para esses cenários, consulte o documento Cenários sem reconhecimento de DI para informações sobre como obter uma instância de um provedor IDataProtection sem passar pela DI.

A amostra a seguir demonstra três conceitos:

  1. Adicionar o sistema de proteção de dados ao contêiner de serviço,

  2. Usando a DI para receber uma instância de um IDataProtectionProvider, e

  3. Criar um IDataProtector a partir de um IDataProtectionProvider e usá-lo para proteger e desproteger dados.

Aplicativo de console

using System;
using Microsoft.AspNetCore.DataProtection;
using Microsoft.Extensions.DependencyInjection;

public class Program
{
    public static void Main(string[] args)
    {
        // add data protection services
        var serviceCollection = new ServiceCollection();
        serviceCollection.AddDataProtection();
        var services = serviceCollection.BuildServiceProvider();

        // create an instance of MyClass using the service provider
        var instance = ActivatorUtilities.CreateInstance<MyClass>(services);
        instance.RunSample();
    }

    public class MyClass
    {
        IDataProtector _protector;

        // the 'provider' parameter is provided by DI
        public MyClass(IDataProtectionProvider provider)
        {
            _protector = provider.CreateProtector("Contoso.MyClass.v1");
        }

        public void RunSample()
        {
            Console.Write("Enter input: ");
            string input = Console.ReadLine();

            // protect the payload
            string protectedPayload = _protector.Protect(input);
            Console.WriteLine($"Protect returned: {protectedPayload}");

            // unprotect the payload
            string unprotectedPayload = _protector.Unprotect(protectedPayload);
            Console.WriteLine($"Unprotect returned: {unprotectedPayload}");
        }
    }
}

/*
 * SAMPLE OUTPUT
 *
 * Enter input: Hello world!
 * Protect returned: CfDJ8ICcgQwZZhlAlTZT...OdfH66i1PnGmpCR5e441xQ
 * Unprotect returned: Hello world!
 */

Aplicativo Web

Chamada AddDataProtection(IServiceCollection, Action<DataProtectionOptions>) em Program.cs:

var builder = WebApplication.CreateBuilder(args);

// Add services to the container.
builder.Services.AddControllersWithViews();
builder.Services.AddDataProtection();

var app = builder.Build();

O código realçado a seguir mostra como usar IDataProtector em um controlador:

public class HomeController : Controller
{
    private readonly IDataProtector _dataProtector;

    public HomeController(IDataProtectionProvider dataProtectionProvider)
    {
        _dataProtector = dataProtectionProvider.CreateProtector("HomeControllerPurpose");
    }

    // ...

    public IActionResult Privacy()
    {
        // The original data to protect
        string originalData = "original data";

        // Protect the data (encrypt)
        string protectedData = _dataProtector.Protect(originalData);
        Console.WriteLine($"Protected Data: {protectedData}");

        // Unprotect the data (decrypt)
        string unprotectedData = _dataProtector.Unprotect(protectedData);
        Console.WriteLine($"Unprotected Data: {unprotectedData}");

        return View();
    }
    
    // ...

O pacote Microsoft.AspNetCore.DataProtection.Abstractions contém um método de extensão GetDataProtector como uma conveniência para o desenvolvedor. Ele encapsula em uma única operação tanto a recuperação de um IDataProtectionProvider do provedor de serviços quanto a chamada de IDataProtectionProvider.CreateProtector. A amostra a seguir demonstra seu uso:

using System;
using Microsoft.AspNetCore.DataProtection;
using Microsoft.Extensions.DependencyInjection;
 
public class Program
{
    public static void Main(string[] args)
    {
        // add data protection services
        var serviceCollection = new ServiceCollection();
        serviceCollection.AddDataProtection();
        var services = serviceCollection.BuildServiceProvider();
 
        // get an IDataProtector from the IServiceProvider
        var protector = services.GetDataProtector("Contoso.Example.v2");
        Console.Write("Enter input: ");
        string input = Console.ReadLine();
 
        // protect the payload
        string protectedPayload = protector.Protect(input);
        Console.WriteLine($"Protect returned: {protectedPayload}");
 
        // unprotect the payload
        string unprotectedPayload = protector.Unprotect(protectedPayload);
        Console.WriteLine($"Unprotect returned: {unprotectedPayload}");
    }
}

Dica

As instâncias de IDataProtectionProvider e IDataProtector são à prova de thread para vários chamadores. A intenção é que uma vez que um componente obtenha uma referência a um IDataProtector por meio de uma chamada a CreateProtector, ele usará essa referência para várias chamadas a Protect e Unprotect. Uma chamada a Unprotect gerará CryptographicException se a carga protegida não puder ser verificada ou decifrada. Alguns componentes podem querer ignorar erros durante operações de desproteção; um componente que lê cookies de autenticações pode lidar com esse erro e tratar a solicitação como se não tivesse nenhum cookie, em vez de falhar a solicitação imediatamente. Os componentes que desejam esse comportamento devem capturar especificamente uma CryptographicException em vez de engolir todas as exceções.