개발 중 민감한 응용 프로그램 정보 안전하게 저장하기

등록일시: 2017-05-19 08:00,  수정일시: 2017-08-24 14:43
조회수: 5,772
이 문서는 ASP.NET Core 기술을 널리 알리고자 하는 개인적인 취지로 제공되는 번역문서입니다. 이 문서에 대한 모든 저작권은 마이크로소프트에 있으며 요청이 있을 경우 언제라도 게시가 중단될 수 있습니다. 번역 내용에 오역이 존재할 수 있고 주석은 번역자 개인의 의견일 뿐이며 마이크로소프트는 이에 관한 어떠한 보장도 하지 않습니다. 번역이 완료된 이후에도 대상 제품 및 기술이 개선되거나 변경됨에 따라 원문의 내용도 변경되거나 보완되었을 수 있으므로 주의하시기 바랍니다.
본문에서는 Secret Manager 도구를 이용해서 개발 중 ASP.NET Core 응용 프로그램의 민감한 데이터를 코드 외부에 저장하는 방법을 살펴봅니다.

본문에서는 개발 시 Secret Manager 도구를 이용해서 민감한 데이터를 코드 외부에 저장하는 방법을 살펴봅니다. 무엇보다 중요한 점은 절대로 소스 코드에 암호나 기타 민감한 데이터를 저장하면 안되며, 운영 환경의 보안 정보를 개발 및 테스트 모드에서 사용해서는 안된다는 것입니다. 대신, 구성 시스템을 이용해서 환경 변수로부터 또는 Secret Manager 도구를 이용해서 저장된 값들로부터 이런 값을 읽어올 수 있습니다. Secret Manager 도구는 민감한 보안 정보가 소스 제어에 체크인되지 않게끔 도와줍니다. 구성 시스템은 Secret Manager 도구를 이용해서 저장된 민감한 정보를 읽을 수 있습니다.

Secret Manager 도구는 개발 시에만 사용됩니다. Azure의 테스트 및 운영 보안 데이터는 Microsoft Azure Key Vault 구성 공급자를 이용해서 보호할 수 있습니다. 보다 자세한 정보는 Azure Key Vault configuration provider 문서를 참고하시기 바랍니다.

환경 변수

응용 프로그램의 보안 정보는 코드나 로컬 구성 파일보다는 환경 변수에 저장해야 합니다. AddEnvironmentVariables를 호출하면 환경 변수에서 값을 읽도록 구성 프레임워크를 설정할 수 있습니다. 그런 다음, 환경 변수를 사용해서 미리 지정된 모든 구성 소스의 구성 값을 재지정 할 수 있습니다.

가령, Visual Studio에서 개별 사용자 계정 옵션으로 새로운 ASP.NET Core 웹 응용 프로그램을 생성하면, 프로젝트의 appsettings.json 파일에 DefaultConnection이라는 키를 가진 기본 연결 문자열이 추가됩니다. 이 기본 연결 문자열은 사용자 모드에서 실행되고 암호를 요구하지 않는 LocalDB를 사용하도록 구성되는데, 응용 프로그램을 테스트 서버나 운영 서버에 배포할 때, DefaultConnection 키의 값을 테스트 또는 프로덕션 데이터베이스 서버의 연결 문자열이 지정된 (민감한 자격 증명이 포함될 수 있는) 환경 변수 설정으로 재정의 할 수 있습니다.

경고

일반적으로 환경 변수는 평문으로 저장되며 암호화되지 않습니다. 머신이나 프로세스가 손상될 경우, 신뢰할 수 없는 사용자가 환경 변수에 접근할 수 있습니다. 따라서 여전히 사용자의 보안 정보 유출을 방지하기 위한 추가적인 방안이 필요할 수도 있습니다.

Secret Manager

Secret Manager 도구는 개발에 필요한 민감한 데이터를 프로젝트의 디렉터리 구조 외부에 저장합니다. Secret Manager 도구는 개발 기간 동안, .NET Core 프로젝트에 사용되는 보안 정보를 저장하기 위해서 사용할 수 있는 프로젝트 도구입니다. Secret Manager 도구를 사용하면, 응용 프로그램의 보안 정보를 특정 프로젝트와 연결하고 이를 여러 프로젝트에서 공유할 수 있습니다.

경고

Secret Manager 도구는 보안 정보를 암호화해서 저장하지 않기 때문에, 신뢰할 수 있는 저장소로 간주하면 안됩니다. 이 도구는 오로지 개발 목적으로만 사용해야 합니다. 키와 값은 사용자 프로파일 디렉터리에 위치한 JSON 구성 파일에 저장됩니다.

Visual Studio 2017: Secret Manager 도구 설치하기

마우스 오른쪽 버튼으로 솔루션 탐색기에서 프로젝트를 클릭한 다음, <project_name>.csproj 편집 (Edit <project_name>.csproj) 메뉴를 선택합니다. 다음에 강조된 라인을 .csproj 파일에 추가하고 저장하면 관련된 NuGet 패키지가 복원됩니다:

<Project Sdk="Microsoft.NET.Sdk.Web">

  <PropertyGroup>
    <TargetFramework>netcoreapp1.1</TargetFramework>
  </PropertyGroup>

  <PropertyGroup>
    <UserSecretsId>My-USER-SECRET-ID-HERE-c23d27a4-eb88</UserSecretsId>
  </PropertyGroup>

  <ItemGroup>
    <Folder Include="wwwroot\" />
  </ItemGroup>
  
  <ItemGroup>
    <PackageReference Include="Microsoft.AspNetCore" Version="1.1.1" />
    <PackageReference Include="Microsoft.Extensions.Configuration.UserSecrets" Version="1.1.1" />
  </ItemGroup>

  <ItemGroup>
    <DotNetCliToolReference Include="Microsoft.Extensions.SecretManager.Tools" Version="1.0.0-msbuild3-final" />
  </ItemGroup>
</Project>

다시 마우스 오른쪽 버튼으로 솔루션 탐색기에서 프로젝트를 클릭한 다음, 컨텍스트 메뉴에서 사용자 암호 관리 (Manage User Secrets) 메뉴를 선택합니다. 그러면 .csproj 파일의 PropertyGroup 노드 하위에 새로운 UserSecretsId 노드가 추가되고, secrets.json 파일이 편집기에서 열립니다.

다음과 같이 새로운 키와 값을 secrets.json 파일에 추가합니다:

{
    "MySecret": "ValueOfMySecret"
}

Visual Studio 2015: Secret Manager 도구 설치하기

프로젝트의 project.json 파일을 엽니다. 그리고 tools 속성에 Microsoft.Extensions.SecretManager.Tools 참조를 추가하고 저장하면 관련된 NuGet 패키지가 복원됩니다:

"tools": {
    "Microsoft.Extensions.SecretManager.Tools": "1.0.0-preview2-final",
    "Microsoft.AspNetCore.Server.IISIntegration.Tools": "1.0.0-preview2-final"
},

마우스 오른쪽 버튼으로 솔루션 탐색기에서 프로젝트를 클릭한 다음, 컨텍스트 메뉴에서 사용자 암호 관리 (Manage User Secrets) 메뉴를 선택합니다. 그러면 project.json 파일에 새로운 userSecretsId 속성이 추가되고, 편집기에 secrets.json 파일이 열립니다.

다음과 같이 새로운 키와 값을 secrets.json 파일에 추가합니다:

{
    "MySecret": "ValueOfMySecret"
}

Visual Studio Code 또는 명령 프롬프트: Secret Manager 도구 설치하기

프로젝트의 .csproj 파일에 Microsoft.Extensions.SecretManager.Tools를 추가하고 dotnet restore를 실행합니다.

<Project Sdk="Microsoft.NET.Sdk.Web">

  <PropertyGroup>
    <TargetFramework>netcoreapp1.1</TargetFramework>
  </PropertyGroup>

  <PropertyGroup>
    <UserSecretsId>My-USER-SECRET-ID-HERE-c23d27a4-eb88</UserSecretsId>
  </PropertyGroup>

  <ItemGroup>
    <Folder Include="wwwroot\" />
  </ItemGroup>
  
  <ItemGroup>
    <PackageReference Include="Microsoft.AspNetCore" Version="1.1.1" />
    <PackageReference Include="Microsoft.Extensions.Configuration.UserSecrets" Version="1.1.1" />
  </ItemGroup>

  <ItemGroup>
    <DotNetCliToolReference Include="Microsoft.Extensions.SecretManager.Tools" Version="1.0.0-msbuild3-final" />
  </ItemGroup>
</Project>

다음 명령을 실행하면 Secret Manager 도구를 테스트 할 수 있습니다:

dotnet user-secrets -h

그러면 Secret Manager 도구의 사용법, 옵션 및 명령 도움말이 표시됩니다.

노트

이와 같이 .csproj 파일의 DotNetCliToolReference 노드에 정의된 도구를 실행하려면 .csproj 파일과 동일한 디렉터리에 위치해 있어야 합니다.

Secret Manager 도구는 사용자 프로필에 저장된 프로젝트 별 구성 설정을 대상으로 동작합니다. 사용자 보안 정보를 사용하려면 프로젝트의 .csproj 파일에 UserSecretsId 값을 지정해야 합니다. UserSecretsId 값은 임의적이긴 하지만 일반적으로 프로젝트에 고유해야 합니다. 개발자는 대부분 UserSecretsId 값에 GUID를 생성해서 지정합니다.

프로젝트의 .csproj 파일에 UserSecretsId를 추가합니다:

<PropertyGroup>
  <UserSecretsId>My-USER-SECRET-ID-HERE-c23d27a4-eb88</UserSecretsId>
</PropertyGroup>

그런 다음, Secret Manager 도구를 사용해서 보안 정보를 설정합니다. 이를테면 프로젝트 디렉터리의 명령 창에 다음과 같이 입력합니다:

dotnet user-secrets set MySecret ValueOfMySecret

다른 디렉터리에서 Secret Manager 도구를 실행할 수도 있지만, --project 옵션을 사용해서 .csproj 파일의 경로를 전달해야 합니다:

dotnet user-secrets set MySecret ValueOfMySecret --project c:\work\WebApp1\src\webapp1

Secret Manager 도구를 사용하면 응용 프로그램의 보안 정보의 목록을 조회하거나 제거하고 초기화시킬 수도 있습니다.

구성을 통해서 사용자 보안 정보에 접근하기

구성 시스템을 통해서 Secret Manager 도구를 이용해서 저장한 사용자 보안 정보에 접근할 수 있습니다. 먼저 프로젝트에 Microsoft.Extensions.Configuration.UserSecrets 패키지를 추가한 다음, dotnet restore를 실행합니다.

그리고 Startup 클래스의 생성자에 사용자 보안 정보 구성 소스를 추가합니다:

using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;

namespace UserSecrets
{
    public class Startup
    {
        string _testSecret = null;
        public Startup(IHostingEnvironment env)
        {
            var builder = new ConfigurationBuilder();

            if (env.IsDevelopment())
            {
                builder.AddUserSecrets<Startup>();
            }

            Configuration = builder.Build();
        }

        public IConfigurationRoot Configuration { get; }

        public void ConfigureServices(IServiceCollection services)
        {
            _testSecret = Configuration["MySecret"];
        }

        public void Configure(IApplicationBuilder app)
        {
            var result = string.IsNullOrEmpty(_testSecret) ? "Null" : "Not Null";
            app.Run(async (context) =>
            {
                await context.Response.WriteAsync($"Secret is {result}");
            });
        }
    }
}

그리고 나면 구성 API를 통해서 사용자 보안 정보에 접근할 수 있습니다:

using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;

namespace UserSecrets
{
    public class Startup
    {
        string _testSecret = null;
        public Startup(IHostingEnvironment env)
        {
            var builder = new ConfigurationBuilder();

            if (env.IsDevelopment())
            {
                builder.AddUserSecrets<Startup>();
            }

            Configuration = builder.Build();
        }

        public IConfigurationRoot Configuration { get; }

        public void ConfigureServices(IServiceCollection services)
        {
            _testSecret = Configuration["MySecret"];
        }

        public void Configure(IApplicationBuilder app)
        {
            var result = string.IsNullOrEmpty(_testSecret) ? "Null" : "Not Null";
            app.Run(async (context) =>
            {
                await context.Response.WriteAsync($"Secret is {result}");
            });
        }
    }
}

Secret Manager 도구의 동작 방식

Secret Manager 도구는 값이 저장되는 위치 및 방법 같은 구현에 관한 세부적인 내용을 추상화시킵니다. 이런 세부적인 내용을 모르더라도 Secret Manager 도구를 사용하는데는 아무 지장도 없습니다. 현재 버전에서는 사용자 프로필 디렉터리의 JSON 구성 파일에 값이 저장됩니다:

  • Windows: %APPDATA%\microsoft\UserSecrets\<userSecretsId>\secrets.json

  • Linux: ~/.microsoft/usersecrets/<userSecretsId>/secrets.json

  • Mac: ~/.microsoft/usersecrets/<userSecretsId>/secrets.json

여기서 <userSecretsId>의 값은 .csproj 파일에 지정된 값입니다.

그러나 Secret Manager 도구로 저장한 데이터의 경로나 형식에 의존해서 코드를 작성하면 안됩니다. 이런 세부적인 구현 사항은 변경될 수 있기 때문입니다. 예를 들어서, 지금은 보안 정보가 암호화되지 않지만 나중에는 암호화 될 수도 있습니다.

추가 자료