다음을 통해 공유


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

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

경고

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

파일 시스템

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

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

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

Azure Storage

NuGet 패키지는Azure.Extensions.AspNetCore.DataProtection.Blobs Azure Blob Storage에 데이터 보호 키를 저장하기 위한 API를 제공합니다. 웹앱의 여러 인스턴스에서 키를 공유할 수 있습니다. 앱은 여러 서버에서 인증 쿠키 또는 CSRF 보호를 공유할 수 있습니다.

참고

.NET 앱에 패키지를 추가하는 방법에 대한 지침은 패키지 사용 작업 흐름(NuGet 문서)패키지 설치 및 관리 섹션에서 확인할 수 있습니다. 올바른 패키지 버전을 NuGet.org에서 확인하세요.

개발자 자격 증명을 사용하여 Azure Key Vault 와 로컬로 상호 작용하려면 Visual Studio에서 스토리지 계정에 로그인하거나 Azure CLI로 로그인합니다. Azure CLI를 아직 설치하지 않은 경우 Azure CLI를 설치하는 방법을 참조하세요. Visual Studio를 사용하지 않는 경우 Visual Studio의 개발자 PowerShell 패널 또는 명령 셸에서 다음 명령을 실행할 수 있습니다.

az login

자세한 내용은 개발자 도구를 사용하여 Azure에 로그인을 참조하세요.

데이터 보호 키를 유지 관리하도록 Azure Blob Storage를 구성합니다.

  • Azure 스토리지 계정을 만듭니다.

  • 데이터 보호 키 파일을 저장할 컨테이너를 만듭니다.

  • Azure 관리 Identity와 역할 기반 액세스 제어(RBAC)를 사용하여 키 스토리지 Blob에 액세스하는 것이 좋습니다. 키 파일을 만들어 스토리지 계정의 컨테이너에 업로드할 필요가 없습니다. 프레임워크에서 파일을 만듭니다. 키 파일의 내용을 검사하려면 포털의 키 행 끝에 있는 상황에 맞는 메뉴의 보기/편집 명령을 사용합니다.

참고

관리 Identity되는 대신 SAS(공유 액세스 서명)와 함께 Blob URI를 사용하려는 경우 텍스트 편집기를 사용하여 로컬 컴퓨터에 XML 키 파일을 만듭니다.

<?xml version="1.0" encoding="utf-8"?>
<repository>
</repository>

키 파일을 스토리지 계정의 컨테이너에 업로드합니다. 포털의 키 행 끝에 있는 상황에 맞는 메뉴의 보기/편집 명령을 사용하여 Blob에 이전 콘텐츠가 포함되어 있음을 확인합니다. 파일을 수동으로 만들면 이후 단계에서 앱을 구성하기 위해 포털에서 SAS를 사용하여 Blob URI를 가져올 수 있습니다.

  • Azure Managed Identity (또는 사용하려는 기존 Managed Identity에 Storage Blob 데이터 기여자 역할을 추가하세요.)를 생성합니다. 배포를 호스팅하는 Azure App Service에 Managed Identity 를 할당합니다.사용자가 할당한>Identity>>추가입니다.

    참고

    Azure CLI 또는 Visual Studio의 Azure 서비스 인증을 사용하여 Blob 액세스를 위해 권한 있는 사용자와 함께 로컬로 앱을 실행하려는 경우 Storage Blob 데이터 기여자 역할을 사용하여 IAM(Access Control)에 개발자 Azure 사용자 계정을 추가합니다. Visual Studio를 통해 Azure CLI를 사용하려면 개발자 PowerShell 패널에서 명령을 실행하고 az login 프롬프트에 따라 테넌트에 인증합니다.

Azure Blob Storage 공급자를 구성하려면 앱의 PersistKeysToAzureBlobStorage 오버로드 중 하나를 호출합니다. 다음 예제에서는 RBAC(역할 기반 액세스 제어)를 위해 Azure Managed TokenCredential 를 사용하여 Blob URI 및 토큰 자격 증명(Identity)을 허용하는 오버로드를 사용합니다.

다른 오버로드는 다음을 기반으로 합니다.

  • Blob URI 및 스토리지 공유 키 자격 증명(StorageSharedKeyCredential)입니다.
  • SAS(공유 액세스 서명)가 있는 Blob URI입니다.
  • 연결 문자열, 컨테이너 이름 및 블롭 이름입니다.
  • 블롭 클라이언트(BlobClient).

Azure SDK의 API 및 인증에 대한 자세한 내용은 Azure Identity 라이브러리를 사용하여 Azure 서비스에 .NET 앱 인증을 참조하세요. 로깅에 대한 지침은 Azure SDK for .NET: 클라이언트 등록 없이 로그 기록하기를 참조하세요. 종속성 주입을 사용하는 앱의 경우, AddAzureClientsCore에 대해 true을 전달하면서 enableLogForwarding을 호출하여 로깅 인프라를 만들고 연결할 수 있습니다.

서비스가 등록된 Program 파일에서:

TokenCredential? credential;

if (builder.Environment.IsProduction())
{
    credential = new ManagedIdentityCredential("{MANAGED IDENTITY CLIENT ID}");
}
else
{
    // Local development and testing only
    DefaultAzureCredentialOptions options = new()
    {
        // Specify the tenant ID to use the dev credentials when running the app locally
        // in Visual Studio.
        VisualStudioTenantId = "{TENANT ID}",
        SharedTokenCacheTenantId = "{TENANT ID}"
    };

    credential = new DefaultAzureCredential(options);
}

builder.Services.AddDataProtection()
    .SetApplicationName("{APPLICATION NAME}")
    .PersistKeysToAzureBlobStorage(new Uri("{BLOB URI}"), credential);

{MANAGED IDENTITY CLIENT ID}: Azure 관리형 Identity 클라이언트 ID (GUID)입니다.

{TENANT ID}: 테넌트 ID입니다.

{APPLICATION NAME}: SetApplicationName 데이터 보호 시스템 내에서 이 앱의 고유한 이름을 설정합니다. 값은 앱 배포에서 일치해야 합니다.

{BLOB URI}: 키 파일에 대한 전체 URI입니다. URI는 키 파일을 만들 때 Azure Storage에서 생성됩니다. SAS를 사용하지 마세요.

대체 SAS(공유 액세스 서명) 접근 방식: Azure Blob Storage의 키 Blob에 액세스하기 위해 Managed Identity 를 사용하는 대신 SAS 토큰으로 Blob URI를 허용하는 오버로드를 호출 PersistKeysToAzureBlobStorage 할 수 있습니다.

builder.Services.AddDataProtection()
    .SetApplicationName("{APPLICATION NAME}")
    .PersistKeysToAzureBlobStorage(new Uri("{BLOB URI WITH SAS}"));

Startup.ConfigureServices의 경우

TokenCredential? credential;

if (_env.IsProduction())
{
    credential = new ManagedIdentityCredential("{MANAGED IDENTITY CLIENT ID}");
}
else
{
    // Local development and testing only
    DefaultAzureCredentialOptions options = new()
    {
        // Specify the tenant ID to use the dev credentials when running the app locally
        // in Visual Studio.
        VisualStudioTenantId = "{TENANT ID}",
        SharedTokenCacheTenantId = "{TENANT ID}"
    };

    credential = new DefaultAzureCredential(options);
}

services.AddDataProtection()
    .SetApplicationName("{APPLICATION NAME}")
    .PersistKeysToAzureBlobStorage(new Uri("{BLOB URI}"), credential);

{MANAGED IDENTITY CLIENT ID}: Azure 관리형 Identity 클라이언트 ID (GUID)입니다.

{TENANT ID}: 테넌트 ID입니다.

{APPLICATION NAME}: SetApplicationName 데이터 보호 시스템 내에서 이 앱의 고유한 이름을 설정합니다. 값은 앱 배포에서 일치해야 합니다.

{BLOB URI}: 키 파일에 대한 전체 URI입니다. URI는 키 파일을 만들 때 Azure Storage에서 생성됩니다. SAS를 사용하지 마세요.

예제:

https://contoso.blob.core.windows.net/data-protection/keys.xml

대체 SAS(공유 액세스 서명) 접근 방식: Azure Blob Storage의 키 Blob에 액세스하기 위해 Managed Identity 를 사용하는 대신 SAS 토큰으로 Blob URI를 허용하는 오버로드를 호출 PersistKeysToAzureBlobStorage 할 수 있습니다.

services.AddDataProtection()
    .SetApplicationName("{APPLICATION NAME}")
    .PersistKeysToAzureBlobStorage(new Uri("{BLOB URI WITH SAS}"));

{APPLICATION NAME}: SetApplicationName 데이터 보호 시스템 내에서 이 앱의 고유한 이름을 설정합니다. 값은 앱 배포에서 일치해야 합니다.

{BLOB URI WITH SAS}: 키 파일을 SAS 토큰과 함께 쿼리 문자열 매개 변수로 저장해야 하는 전체 URI입니다. 업로드된 키 파일에 대한 SAS를 요청할 때 Azure Storage에서 URI를 생성합니다. 다음 예제에서는 컨테이너 이름이 data-protection고 스토리지 계정 이름은 .입니다 contoso. 키 파일의 이름은 keys.xml. SAS(공유 액세스 서명) 쿼리 문자열은 URI({SHARED ACCESS SIGNATURE} 자리 표시자)의 끝에 있습니다.

예제:

https://contoso.blob.core.windows.net/data-protection/keys.xml{SHARED ACCESS SIGNATURE}

웹앱이 Azure 서비스로 실행되는 경우 다음 예제와 같이 연결 문자열을 사용하여 BlobContainerClientAzure Storage에 인증할 수 있습니다.

경고

이 문서에서는 연결 문자열 사용을 보여 줍니다. 로컬 데이터베이스를 사용하면 사용자를 인증할 필요가 없지만 프로덕션 환경에서는 연결 문자열 인증할 암호를 포함하는 경우가 있습니다. ROPC(리소스 소유자 암호 자격 증명)는 프로덕션 데이터베이스에서 피해야 하는 보안 위험입니다. 프로덕션 앱은 사용 가능한 가장 안전한 인증 흐름을 사용해야 합니다. 테스트 또는 프로덕션 환경에 배포된 앱의 인증에 대한 자세한 내용은 보안 인증 흐름을 참조 하세요.

컨테이너가 없는 경우, CreateIfNotExistsAsync의 선택적 호출은 컨테이너를 자동으로 프로비전합니다.

스토리지 계정에 대한 연결 문자열({CONNECTION STRING} 자리 표시자)은 Entra 또는 Azure Portal의 "액세스 키" 섹션 또는 다음 Azure CLI 명령을 실행하여 찾을 수 있습니다.

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

서비스가 등록된 Program 파일에서:

string connectionString = "{CONNECTION STRING}";
string containerName = "{CONTAINER NAME}";
string blobName = "keys.xml";
var container = new BlobContainerClient(connectionString, containerName);
await container.CreateIfNotExistsAsync();
BlobClient blobClient = container.GetBlobClient(blobName);

builder.Services.AddDataProtection().PersistKeysToAzureBlobStorage(blobClient);

Startup.ConfigureServices의 경우

string connectionString = "{CONNECTION STRING}";
string containerName = "{CONTAINER NAME}";
string blobName = "keys.xml";
var container = new BlobContainerClient(connectionString, containerName);
await container.CreateIfNotExistsAsync();
BlobClient blobClient = container.GetBlobClient(blobName);

services.AddDataProtection().PersistKeysToAzureBlobStorage(blobClient);

Redis

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

Microsoft.AspNetCore.DataProtection.Redis 패키지를 사용하면 Redis 캐시에 데이터 보호 키를 저장할 수 있습니다. 웹앱의 여러 인스턴스에서 키를 공유할 수 있습니다. 앱은 여러 서버에서 인증 쿠키 또는 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");
}

경고

Redis를 사용하여 데이터 보호 키를 유지하는 경우 Redis는 다시 시작할 때 기본적으로 데이터를 유지하지 않습니다. 이로 인해 데이터 보호에서 새 키를 발급하여 이전에 보호된 데이터가 무효화될 수 있습니다.

데이터 지속성이 완화되도록 Redis를 구성할 수 있습니다. Redis 설명서에는 지속성을 구성하는 방법에 대한 정보가 있습니다. Azure Managed Redis를 사용하는 경우 데이터 지속성을 사용하도록 설정했는지 확인합니다. Azure Cache for Redis 는 데이터 지속성을 사용하도록 설정하려면 프리미엄 이상의 계층이 필요합니다.

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

등록부

Windows 배포에만 적용됩니다.

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

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

중요한

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 토론 이슈에서 알려주세요.

제네릭 매개 변수 TContextDbContext을(를) 상속하고 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)
Xml string nvarchar(MAX)

사용자 지정 키 리포지토리

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