ASP.NET Core에 대한 소비자 API 개요

IDataProtectionProviderIDataProtector 인터페이스는 소비자가 데이터 보호 시스템을 사용하는 기본 인터페이스입니다. Microsoft.AspNetCore.DataProtection.Abstractions 패키지에 있습니다.

IDataProtectionProvider

공급자 인터페이스는 데이터 보호 시스템의 루트를 나타냅니다. 데이터를 보호하거나 보호 해제하는 데는 직접 사용할 수 없습니다. 대신 소비자는 IDataProtectionProvider.CreateProtector(purpose)를 호출하여 IDataProtector에 대한 참조를 받아야 합니다. 여기서 용도는 의도한 소비자 사용 사례를 설명하는 문자열입니다. 이 매개 변수의 의도 및 적절한 값을 선택하는 방법에 대한 자세한 내용은 용도 문자열을 참조하세요.

IDataProtector

보호기 인터페이스는 CreateProtector에 대한 호출에 의해 반환되며, 소비자가 보호 및 보호 해제 작업을 수행하는 데 사용할 수 있는 이 인터페이스입니다.

데이터 조각을 보호하려면 Protect 메서드에 데이터를 전달합니다. 기본 인터페이스에서는 바이트[] -> 바이트[]로 변환하는 메서드를 정의하지만, 스트링 -> 스트링으로 변환하는 오버로드(확장 메서드로서 제공)도 있습니다. 두 메서드에서 제공하는 보안은 동일합니다. 개발자는 사용 사례에 가장 편리한 오버로드를 선택해야 합니다. 선택한 오버로드에 관계없이 Protect 메서드에서 반환된 값은 이제 보호되고(암호화 및 변조 방지) 애플리케이션에서 신뢰할 수 없는 클라이언트로 보낼 수 있습니다.

이전에 보호된 데이터 조각을 보호 해제하려면 보호된 데이터를 Unprotect 메서드에 전달합니다. (개발자 편의를 위해 바이트[] 기반 및 문자열 기반 오버로드가 있습니다.) 이 동일한 IDataProtector에 대한 이전 Protect 호출에 의해 보호된 페이로드가 생성된 경우 Unprotect 메서드는 원래 보호되지 않은 페이로드를 반환합니다. 보호된 페이로드가 변조되었거나 다른 IDataProtector에 의해 생성된 경우 Unprotect 메서드는 CryptographicException을 throw합니다.

동일한 개념과 다른 개념 IDataProtector는 용도의 개념과 다시 연결합니다. 두 IDataProtector 인스턴스가 동일한 루트IDataProtectionProvider에서 생성되었지만 IDataProtectionProvider.CreateProtector에 대한 호출에서 다른 용도 문자열을 통해 생성된 경우 다른 보호기로 간주되며 다른 인스턴스에서 생성된 페이로드를 보호 해제할 수 없습니다.

이러한 인터페이스 사용

DI 인식 구성 요소의 경우 용도는 구성 요소가 해당 생성자에서 IDataProtectionProvider 매개 변수를 사용하며 구성 요소가 인스턴스화될 때 DI 시스템에서 이 서비스를 자동으로 제공하는 것입니다.

참고 항목

일부 애플리케이션(예: 콘솔 애플리케이션 또는 ASP.NET 4.x 애플리케이션)은 DI를 인식하지 못할 수 있으므로 여기에 설명된 메커니즘을 사용할 수 없습니다. 이러한 시나리오의 경우 DI를 거치지 않고 IDataProtection 공급자 인스턴스를 얻는 데 대한 자세한 내용은 비 DI 인식 시나리오 문서를 참조하세요.

다음 샘플에서는 세 가지 개념을 보여 줍니다.

  1. 서비스 컨테이너에 데이터 보호 시스템 추가

  2. DI를 사용하여 IDataProtectionProvider의 인스턴스 받기 및

  3. IDataProtectionProvider에서 IDataProtector를 만들고 이를 사용하여 데이터를 보호 및 보호 해제

콘솔 앱

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

웹 앱

Program.cs에서 AddDataProtection(IServiceCollection, Action<DataProtectionOptions>)을 호출합니다.

var builder = WebApplication.CreateBuilder(args);

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

var app = builder.Build();

다음 강조 표시된 코드는 컨트롤러에서 사용하는 IDataProtector 방법을 보여줍니다.

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();
    }
    
    // ...

패키지 Microsoft.AspNetCore.DataProtection.Abstractions 에는 개발자 편의를 위해 확장 메서드 GetDataProtector 가 포함되어 있습니다. 서비스 공급자에서 IDataProtectionProvider를 검색하고 IDataProtectionProvider.CreateProtector를 호출하는 단일 작업으로 캡슐화됩니다. 다음 샘플에서는 사용량을 보여 줍니다.

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

IDataProtectionProviderIDataProtector의 인스턴스는 여러 호출자에 대해 스레드로부터 안전합니다. 구성 요소가 CreateProtector에 대한 호출을 통해 IDataProtector에 대한 참조를 가져오면 ProtectUnprotect에 대한 여러 호출에 해당 참조를 사용합니다. Unprotect에 대한 호출은 보호된 페이로드를 확인하거나 해독할 수 없는 경우 CryptographicException을 throw합니다. 일부 구성 요소는 보호 해제 작업 중에 오류를 무시하려고 할 수 있습니다. 인증 cookie를 읽는 구성 요소는 이 오류를 처리하고 요청을 완전히 실패하는 대신 cookie가 전혀 없는 것처럼 처리할 수 있습니다. 이 동작을 원하는 구성 요소는 모든 예외를 발생시키는 대신 특별히 CryptographicException을 catch해야 합니다.