데이터 보호: 소비자 APIs 개요

등록일시: 2017-03-31 08:00,  수정일시: 2017-05-22 01:14
조회수: 4,367
이 문서는 ASP.NET Core 기술을 널리 알리고자 하는 개인적인 취지로 제공되는 번역문서입니다. 이 문서에 대한 모든 저작권은 마이크로소프트에 있으며 요청이 있을 경우 언제라도 게시가 중단될 수 있습니다. 번역 내용에 오역이 존재할 수 있고 주석은 번역자 개인의 의견일 뿐이며 마이크로소프트는 이에 관한 어떠한 보장도 하지 않습니다. 번역이 완료된 이후에도 대상 제품 및 기술이 개선되거나 변경됨에 따라 원문의 내용도 변경되거나 보완되었을 수 있으므로 주의하시기 바랍니다.
본문에서는 데이터 보호 시스템을 사용할 때 가장 기본이 되는 IDataProtectionProvider 인터페이스와 IDataProtector 인터페이스에 관해서 살펴봅니다.

IDataProtectionProvider 인터페이스와 IDataProtector 인터페이스는 소비자가 데이터 보호 시스템을 사용하기 위한 가장 기본이 되는 인터페이스들입니다. 이 인터페이스들은 Microsoft.AspNetCore.DataProtection.Abstractions 패키지에 위치해 있습니다.

IDataProtectionProvider

공급자 인터페이스는 데이터 보호 시스템의 루트를 나타냅니다. 그러나 이 인터페이스를 데이터 보호 및 보호 해제 작업에 직접 사용할 수는 없습니다. 그 대신, 소비자는 IDataProtectionProvider.CreateProtector("용도") 메서드를 호출해서 IDataProtector 인터페이스의 참조를 가져와야 하며, 여기서 "용도"는 사용 사례에 관한 소비자의 의도를 설명하는 문자열입니다. 이 매개변수의 의미와 적절한 값을 선택하는 요령에 대해서는 용도 문자열 문서를 참고하시기 바랍니다.

IDataProtector

보호자 인터페이스는 CreateProtector 메서드 호출에 의해서 반환되며 소비자가 보호 및 보호 해제 작업에 사용할 수 있는 인터페이스입니다.

데이터를 보호하려면 데이터를 Protect 메서드에 전달하기만 하면 됩니다. 기본 인터페이스는 byte[]을 byte[]로 변환하는 메서드를 정의하지만, 문자열을 문자열로 변환하는 오버로드 메서드도 (확장 메서드의 형태로) 제공됩니다. 두 메서드의 결과는 보안적으로 동일하므로 개발자는 자신의 상황에 가장 적합한 오버로드 버전을 선택하면 됩니다. 어떤 오버로드 메서드를 선택하던지 Protect 메서드에서 반환된 값은 보호되며 (암호화 및 변조 방지되며), 응용 프로그램은 신뢰할 수 없는 클라이언트로 이 값을 전송할 수 있습니다.

이미 보호된 데이터를 보호 해제하려면 보호된 데이터를 Unprotect 메서드에 전달하면 됩니다. (개발자의 편의를 위해서 byte[]을 byte[]로 변환하는 메서드와 문자열을 문자열로 변환하는 메서드가 모두 제공됩니다.) 동일한 IDataProtector 인스턴스의 Protect 메서드를 호출해서 생성된 보호된 페이로드는 Unprotect 메서드를 호출하면 보호 해제된 원본 페이로드가 반환됩니다. 그러나 보호된 페이로드가 변조됐거나 다른 IDataProtector 인스턴스에 의해서 만들어진 페이로드라면 Unprotect 메서드가 CryptographicException을 던집니다.

IDataProtector 인스턴스의 동일 여부를 판단하는 기준은 용도의 개념과 밀접한 관계를 갖고 있습니다. 비록 두 IDataProtector 인스턴스가 동일한 루트 IDataProtectionProvider로부터 생성되었더라도, IDataProtectionProvider.CreateProtector 메서드를 호출할 때 서로 다른 용도 문자열을 지정했다면, 두 인스턴스는 별개의 보호자로 간주되며, 서로 다른 인스턴스가 보호한 페이로드를 보호 해제하지 못합니다.

인터페이스 소비하기

DI-인식 구성 요소의 경우, 구성 요소의 생성자에서 IDataProtectionProvider 매개변수를 전달 받고, 구성 요소의 인스턴스가 만들어질 때 DI 시스템이 자동으로 서비스를 제공하는 방식으로 사용하도록 만들어졌습니다.

노트

일부 응용 프로그램은 (콘솔 응용 프로그램이나 ASP.NET 4.x 응용 프로그램 같은) DI를 인식하지 못하므로 본문에서 설명하는 메커니즘을 사용할 수 없습니다. 이런 시나리오를 위해서 DI를 거치지 않고 IDataProtectionProvider의 인스턴스를 가져오는 방법에 대한 자세한 정보는 비-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!
 */

Microsoft.AspNetCore.DataProtection.Abstractions 패키지에는 개발자의 편의를 위한 IServiceProvider.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}");
    }
}

IDataProtectionProvider 인터페이스 및 IDataProtector 인터페이스의 인스턴스는 다중 호출자에 대해 스레드로부터 안전(Thread-Safe)합니다. 구성 요소에서 CreateProtector 메서드를 호출해서 IDataProtector 인터페이스 참조를 얻은 다음, 이 참조를 이용해서 Protect 메서드 및 Unprotect 메서드를 반복적으로 호출할 수 있도록 만들어졌습니다.

Unprotect 메서드 호출 시 보호된 페이로드를 검증하거나 판독하지 못하면 CryptographicException이 던져집니다. 일부 구성 요소에서는 보호 해제 작업 중 발생하는 예외를 무시해야 하는 경우도 있는데, 가령 인증 쿠키를 읽는 구성 요소는 요청 자체를 실패로 처리하는 대신 이 예외를 잡아서 처리함으로써 쿠키가 아예 존재하지 않는 것처럼 동작할 수 있습니다. 이런 동작이 필요한 구성 요소는 모든 예외를 감춰버리는 대신 명확하게 CryptographicException만 잡아야 합니다.