ASP.NET Core의 키 스토리지 공급자

데이터 보호 시스템은 기본적으로 검색 메커니즘을 사용하여 암호화 키를 유지해야 하는 위치를 결정합니다. 개발자는 기본 검색 메커니즘을 재정의하고 위치를 수동으로 지정할 수 있습니다.

Warning

명시적 키 지속성 위치를 지정하는 경우 데이터 보호 시스템은 미사용 기본 키 암호화 메커니즘의 등록을 취소하므로 키는 더 이상 미사용 시 암호화되지 않습니다. 프로덕션 배포에 대한 명시적 키 암호화 메커니즘을 추가로 지정하는 것이 좋습니다.

파일 시스템

파일 시스템 기반 키 리포지토리를 구성하려면 아래와 같이 구성 루틴을 PersistKeysToFileSystem 호출합니다. DirectoryInfo 키를 저장해야 하는 리포지토리를 가리킵니다.

public void ConfigureServices(IServiceCollection services)
{
    services.AddDataProtection()
        .PersistKeysToFileSystem(new DirectoryInfo(@"c:\temp-keys\"));
}

DirectoryInfo는 로컬 머신의 디렉터리를 가리키거나 네트워크 공유의 폴더를 가리킬 수 있습니다. 로컬 머신의 디렉터리를 가리키는 경우(로컬 머신의 앱만 이 리포지토리를 사용하기 위해 액세스해야 하는 시나리오) Windows DPAPI(Windows)를 사용하여 미사용 키를 암호화하는 것이 좋습니다. 그렇지 않으면 X.509 인증서를 사용하여 미사용 키를 암호화하는 것이 좋습니다.

Azure Storage

Azure.Extensions.AspNetCore.DataProtection.Blobs 패키지를 사용하면 Azure Blob Storage의 데이터 보호 키를 저장할 수 있습니다. 웹앱의 여러 인스턴스에서 키를 공유할 수 있습니다. 앱은 여러 서버에서 인증 cookie 또는 CSRF 보호를 공유할 수 있습니다.

Azure Blob Storage 공급자를 구성하려면 오버로드 중 PersistKeysToAzureBlobStorage 하나를 호출합니다.

public void ConfigureServices(IServiceCollection services)
{
    services.AddDataProtection()
        .PersistKeysToAzureBlobStorage(new Uri("<blob URI including SAS token>"));
}

웹앱이 Azure 서비스로 실행되는 경우 연결 문자열을 사용하여 Azure.Storage.Blobs를 통해 Azure Storage에 인증할 수 있습니다.

string connectionString = "<connection_string>";
string containerName = "my-key-container";
string blobName = "keys.xml";
BlobContainerClient container = new BlobContainerClient(connectionString, containerName);

// optional - provision the container automatically
await container.CreateIfNotExistsAsync();

BlobClient blobClient = container.GetBlobClient(blobName);

services.AddDataProtection()
    .PersistKeysToAzureBlobStorage(blobClient);

참고 항목

스토리지 계정에 대한 연결 문자열은 "액세스 키" 섹션 아래의 Azure Portal 또는 다음 CLI 명령을 실행하여 찾을 수 있습니다.

az storage account show-connection-string --name <account_name> --resource-group <resource_group>

Redis

Microsoft.AspNetCore.DataProtection.StackExchangeRedis 패키지를 사용하면 Redis 캐시에 데이터 보호 키를 저장할 수 있습니다. 웹앱의 여러 인스턴스에서 키를 공유할 수 있습니다. 앱은 여러 서버에서 인증 cookie 또는 CSRF 보호를 공유할 수 있습니다.

Microsoft.AspNetCore.DataProtection.Redis 패키지를 사용하면 Redis 캐시에 데이터 보호 키를 저장할 수 있습니다. 웹앱의 여러 인스턴스에서 키를 공유할 수 있습니다. 앱은 여러 서버에서 인증 cookie 또는 CSRF 보호를 공유할 수 있습니다.

Redis에서 구성하려면 오버로드 중 PersistKeysToStackExchangeRedis 하나를 호출합니다.

public void ConfigureServices(IServiceCollection services)
{
    var redis = ConnectionMultiplexer.Connect("<URI>");
    services.AddDataProtection()
        .PersistKeysToStackExchangeRedis(redis, "DataProtection-Keys");
}

Redis에서 구성하려면 오버로드 중 PersistKeysToRedis 하나를 호출합니다.

public void ConfigureServices(IServiceCollection services)
{
    var redis = ConnectionMultiplexer.Connect("<URI>");
    services.AddDataProtection()
        .PersistKeysToRedis(redis, "DataProtection-Keys");
}

자세한 내용은 아래 항목을 참조하세요.

등록

Windows 배포에만 적용됩니다.

앱에 파일 시스템에 대한 쓰기 권한이 없는 경우도 있습니다. 앱이 가상 서비스 계정(예: w3wp.exe의 앱 풀 ID)으로 실행되는 시나리오를 고려합니다. 이러한 경우 관리자는 서비스 계정 ID로 액세스할 수 있는 레지스트리 키를 프로비전할 수 있습니다. PersistKeysToRegistry 아래와 같이 확장 메서드를 호출합니다. RegistryKey 암호화 키를 저장해야 하는 위치를 가리킵니다.

public void ConfigureServices(IServiceCollection services)
{
    services.AddDataProtection()
        .PersistKeysToRegistry(Registry.CurrentUser.OpenSubKey(@"SOFTWARE\Sample\keys", true));
}

Important

Windows DPAPI를 사용하여 미사용 키를 암호화하는 것이 좋습니다.

Entity Framework Core

Microsoft.AspNetCore.DataProtection.EntityFrameworkCore 패키지는 Entity Framework Core를 사용하여 데이터베이스에 데이터 보호 키를 저장하는 메커니즘을 제공합니다. Microsoft.AspNetCore.DataProtection.EntityFrameworkCore NuGet 패키지는 프로젝트 파일에 추가해야 하며, Microsoft.AspNetCore.App 메타패키지의 일부가 아닙니다.

이 패키지를 사용하면 웹앱의 여러 인스턴스에서 키를 공유할 수 있습니다.

EF Core 공급자를 구성하려면 PersistKeysToDbContext 메서드를 호출합니다.

public void ConfigureServices(IServiceCollection services)
{
    services.Configure<CookiePolicyOptions>(options =>
    {
        options.CheckConsentNeeded = context => true;
        options.MinimumSameSitePolicy = SameSiteMode.None;
    });

    services.AddDbContext<ApplicationDbContext>(options =>
        options.UseSqlServer(
            Configuration.GetConnectionString("DefaultConnection")));

    // Add a DbContext to store your Database Keys
    services.AddDbContext<MyKeysContext>(options =>
        options.UseSqlServer(
            Configuration.GetConnectionString("MyKeysConnection")));

    // using Microsoft.AspNetCore.DataProtection;
    services.AddDataProtection()
        .PersistKeysToDbContext<MyKeysContext>();

    services.AddDefaultIdentity<IdentityUser>()
        .AddDefaultUI(UIFramework.Bootstrap4)
        .AddEntityFrameworkStores<ApplicationDbContext>();
    services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
}

영어 이외의 언어로 번역된 코드 주석을 보려면 이 GitHub 토론 이슈에서 알려주세요.

제네릭 매개 변수는 TContext다음에서 DbContext 상속하고 구현 IDataProtectionKeyContext해야 합니다.

using Microsoft.AspNetCore.DataProtection.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore;
using WebApp1.Data;

namespace WebApp1
{
    class MyKeysContext : DbContext, IDataProtectionKeyContext
    {
        // A recommended constructor overload when using EF Core 
        // with dependency injection.
        public MyKeysContext(DbContextOptions<MyKeysContext> options) 
            : base(options) { }

        // This maps to the table that stores keys.
        public DbSet<DataProtectionKey> DataProtectionKeys { get; set; }
    }
}

DataProtectionKeys 테이블을 만듭니다.

PMC(패키지 관리자 콘솔) 창에서 다음 명령을 수행합니다.

Add-Migration AddDataProtectionKeys -Context MyKeysContext
Update-Database -Context MyKeysContext

MyKeysContext는 이전 코드 샘플에서 정의된 DbContext입니다. 다른 이름의 DbContext를 사용하는 경우 DbContext 이름을 MyKeysContext로 대체합니다.

DataProtectionKeys 클래스/엔터티는 다음 표에 표시된 구조체를 채택합니다.

속성/필드 CLR 형식 SQL 형식
Id int int, PK, IDENTITY(1,1), Null이 아님
FriendlyName string nvarchar(MAX), null
Xml string nvarchar(MAX), null

사용자 지정 키 리포지토리

기본 제공 메커니즘이 적절하지 않은 경우 개발자는 사용자 지정 IXmlRepository을 제공하여 고유한 키 지속성 메커니즘을 지정할 수 있습니다.