Introducción a las API de consumidor para ASP.NET Core

Las interfaces de IDataProtectionProvider y IDataProtector son las interfaces básicas a través de las cuales los consumidores utilizan el sistema de protección de datos. Se encuentran en el paquete Microsoft.AspNetCore.DataProtection.Abstractions .

IDataProtectionProvider

La interfaz del proveedor representa la raíz del sistema de protección de datos. No se puede usar directamente para proteger o desproteger datos. En su lugar, el consumidor debe obtener una referencia a un IDataProtector llamando a IDataProtectionProvider.CreateProtector(purpose), donde el propósito es una cadena que describe el caso de uso previsto por el consumidor. Consulte Cadenas de propósito para obtener más información sobre la intención de este parámetro y cómo elegir un valor adecuado.

IDataProtector

La interfaz del protector se devuelve mediante una llamada a CreateProtector, y es esta interfaz la que los consumidores pueden utilizar para realizar operaciones de protección y desprotección.

Para proteger un fragmento de datos, páselo al método Protect. La interfaz básica define un método que convierte byte[] -> byte[], pero también hay una sobrecarga (proporcionada como un método de extensión) que convierte string -> string. La seguridad ofrecida por los dos métodos es idéntica; el desarrollador debe elegir la sobrecarga más conveniente para su caso de uso. Independientemente de la sobrecarga elegida, el valor devuelto por el método de protección está ahora protegido (cifrado y a prueba de manipulaciones), y la aplicación puede enviarlo a un cliente que no sea de confianza.

Para desproteger un fragmento de datos protegido anteriormente, pase los datos protegidos al método Unprotect. (Hay sobrecargas basadas en bytes[] y basadas en cadenas para la comodidad del desarrollador). Si la carga protegida se generó mediante una llamada anterior a Protect en este mismo IDataProtector, el método Unprotect devolverá la carga desprotegida original. Si la carga protegida se ha manipulado o se ha producido mediante otro IDataProtector, el método Unprotect iniciará CryptographicException.

El concepto de igual frente a diferente IDataProtector está relacionado con el concepto de propósito. Si dos instancias de IDataProtector se generaron a partir de la misma raíz IDataProtectionProvider pero a través de diferentes cadenas de propósito en la llamada a IDataProtectionProvider.CreateProtector, entonces se consideran protectores diferentes, y uno no podrá desproteger las cargas útiles generadas por el otro.

Consumo de estas interfaces

Para un componente compatible con DI, el uso previsto es que el componente toma un parámetro IDataProtectionProvider en su constructor y que el sistema de inserción de dependencias proporciona automáticamente este servicio cuando se crea una instancia del componente.

Nota:

Es posible que algunas aplicaciones (como aplicaciones de consola o de ASP.NET 4.x) no sean compatibles con DI, por lo que no puede usar el mecanismo descrito aquí. Para estos escenarios, consulte el documento Escenarios no compatibles con DI para obtener más información sobre cómo obtener una instancia de un proveedor de IDataProtection sin pasar por la inserción de dependencias.

En el ejemplo siguiente se muestran tres conceptos:

  1. Agregue el sistema de protección de datos al contenedor de servicios,

  2. Uso de DI para recibir una instancia de IDataProtectionProvider y

  3. Creación de un IDataProtector a partir de un IDataProtectionProvider y su uso para proteger y desproteger datos.

Aplicación de consola

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

Aplicación web

Llame a AddDataProtection(IServiceCollection, Action<DataProtectionOptions>) en Program.cs:

var builder = WebApplication.CreateBuilder(args);

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

var app = builder.Build();

El código resaltado siguiente muestra cómo usar IDataProtector en un 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();
    }
    
    // ...

El paquete Microsoft.AspNetCore.DataProtection.Abstractions contiene un método GetDataProtector de extensión como comodidad para desarrolladores. Se encapsula como una sola operación que recupera un IDataProtectionProvider del proveedor de servicios y llama a IDataProtectionProvider.CreateProtector. En el ejemplo siguiente se muestra su 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}");
    }
}

Sugerencia

Las instancias de IDataProtectionProvider y IDataProtector son seguras para subprocesos para varios autores de llamada. La intención es que una vez que un componente obtiene una referencia a un IDataProtector a través de una llamada a CreateProtector, utilizará esa referencia para múltiples llamadas a Protect y Unprotect. Una llamada a Unprotect iniciará CryptographicException si la carga protegida no se puede comprobar ni descifrar. Algunos componentes pueden querer ignorar los errores durante las operaciones de desprotección; un componente que lee cookies de autenticación podría manejar este error y tratar la solicitud como si no tuviera cookie en lugar de rechazar la solicitud directamente. Los componentes que quieran este comportamiento deberían atrapar específicamente CryptographicException en lugar de tragarse todas las excepciones.