Windows 앱에서 설정 파일에 기밀 정보를 평문으로 저장하지 않기 위한 베스트 프랙티스

· · Windows 개발, 보안, DPAPI, C# / .NET, Win32

앞선 글 「Windows 앱 개발에서 최소한의 보안을 지키기 위한 체크리스트」 에서,
「비밀 정보를 소스 코드나 평문 설정에 두지 않는다」「Win32 / .NET이라면 DPAPI / ProtectedData를 쓴다」라는 최소 선을 썼습니다.

이번에는 그중에서도 「DPAPI로 최소한 평문보다는 낫게」 를 조금 더 깊이 파고듭니다.

대상은 다음과 같은 Windows 앱입니다.

  • WPF / WinForms / WinUI 데스크톱 앱
  • C# / .NET의 Windows 클라이언트
  • 로컬 설정 파일에 접속처 자격 증명이나 API 토큰을 저장하고 싶어지는 앱

여기서 다루는 것은 「로컬에 저장할 수밖에 없는 비밀을, 적어도 appsettings.json의 평문 그대로 내버려두지 않기」 위한 현실적 설계입니다.
「어떤 공격자에게도 이기는 완벽 방어」의 이야기가 아닙니다. 그쪽까지 말을 부풀리면 보안이 갑자기 괴담이 됩니다.

1. 먼저 결론

먼저 결론을 쓰자면, 실무에서는 다음 순서로 생각하면 깔끔합니다.

  1. 애초에 클라이언트에 장기 비밀을 두지 않는다
    • Windows 인증, 통합 인증, 사용자의 대화형 로그인, 서버 측 비밀 관리를 우선
  2. 어쩔 수 없이 로컬 저장이 필요하다면 평문으로 두지 않는다
    • Windows라면 먼저 DPAPI / ProtectedData를 제1 후보로
  3. 일반 데스크톱 앱은 DataProtectionScope.CurrentUser를 기본으로
    • LocalMachine은 용도가 꽤 제한적
  4. DPAPI는 「단말이 완전히 침해된 상황」까지 지키는 것은 아니다
    • 같은 사용자 권한으로 도는 코드는 기본적으로 그 사용자가 복호화할 수 있는 것을 복호화할 수 있다

그리고 이 글에서 가장 중요한 논점은 여기입니다.

「어차피 비밀키는 어딘가에 저장해야 하니까, 평문이든 DPAPI든 보안상으로는 같은 것 아닌가?」

이건 절반은 맞고, 결론은 틀렸습니다.

  • 자체 AES + 키를 같은 앱이나 같은 설정에 두는 경우는 꽤 평문에 가깝습니다
  • 하지만 DPAPI는 키 관리를 OS에 맡기고, 복호화할 수 있는 주체를 「그 Windows 사용자」 또는 「그 컴퓨터」에 묶습니다
  • 그 결과, 설정 파일 단체의 유출, 다른 PC로의 반출, 오송, 백업 유출, 리포지토리 혼입 같은 사고에 대한 내성이 크게 달라집니다

즉,
「키가 어딘가에 있다」는 추상론만 보면 같아 보여도,
「누가·어떤 맥락에서·얼마나 쉽게 쓸 수 있는가」가 전혀 다르다
는 이야기입니다.

현관 매트 밑에 열쇠를 두는 것과, 관리실에서 본인 확인을 거쳐 열쇠를 내주는 것을 같다고 단언하는 건 조금 거친 이야기입니다.

2. 왜 평문 설정이 위험한가

평문 저장이 위험한 이유는 암호 이론보다 훨씬 흙 묻은 이야기입니다. 실무에서는 대체로 다음과 같은 경로로 샙니다.

  • 설정 파일을 그대로 Git에 넣어 버린다
  • 장애 조사용 ZIP에 설정 파일이 통째로 들어간다
  • 지원 문의에 설정 파일을 첨부하게 한다
  • 백업이나 파일 공유로 제삼자가 읽을 수 있다
  • 로그에 접속 문자열이나 토큰이 그대로 찍힌다
  • 퇴사자나 다른 사용자가 같은 단말의 파일을 읽을 수 있다

평문의 최대 문제는 「읽힌 순간 비밀로서 끝」 이라는 점입니다.

  • 파일을 열면 끝
  • 복사하면 끝
  • 메일에 첨부되면 끝
  • 리포지토리에 남으면 반영구적으로 뒷감당

공격자가 고도의 기술을 가질 필요조차 없습니다.
텍스트 에디터로 열린다는 것만으로 이미 꽤 약한 상태입니다.

3. 「비밀키는 어딘가에 저장되는 거니까 같은 것 아닌가?」에 대한 답

이 의문은 타당합니다.
그리고 여기를 대충 답하면 보안 글이 갑자기 두루뭉술해집니다.

결론부터 말하면, 「어딘가에 키가 필요하다」라는 의미에서는 yes, 그러니까 같다는 의미에서는 no 입니다.

3.1. 무엇이 같고, 무엇이 다른가

확실히 암호화에는 최종적으로 어떤 root of trust가 필요합니다.
비밀은 우주 어딘가에서 공짜로 솟아나지 않습니다. 그 부분은 엄격한 세계입니다.

다만, 보안상의 차이는 다음 3점으로 결정됩니다.

  • 키를 앱이 직접 가지는가
  • 키가 어떤 주체에 묶여 있는가
  • 파일만 도둑맞았을 때 복호화할 수 있는가

이 차이를 대충 표로 정리하면 이렇습니다.

방식 설정 파일이 읽혔을 때 파일만 다른 PC로 반출됨 같은 PC의 다른 사용자에게 읽힘 같은 사용자 권한으로 도는 코드
평문 그 자리에서 샘 그대로 샘 그대로 샘 당연히 읽힘
자체 암호화 + 키를 같은 설정/바이너리에 둠 꽤 샘 꽤 샘 꽤 샘 당연히 복호화됨
DPAPI + CurrentUser 파일 단체로는 바로 안 읽힘 통상 복호화 어려움 통상 복호화 어려움 복호화 가능
DPAPI + LocalMachine 파일 단체로는 바로 안 읽힘 그 PC 이외에서는 통상 복호화 어려움 같은 PC라면 폭넓게 복호화 가능 복호화 가능

여기서 중요한 건 DPAPI가 「파일을 읽을 수 있다」와 「비밀을 쓸 수 있다」를 분리한다 는 점입니다.

평문에서는 이 둘이 같습니다.
파일이 읽히면 비밀도 읽힙니다.

하지만 DPAPI에서는 적어도 CurrentUser라면,

  • 그 Windows 사용자로서
  • 그 Windows 맥락에서
  • OS의 보호 기구를 거쳐서

복호화해야 합니다.

이 차이는 사고 현장에서는 꽤 큽니다.

3.2. 「그래도 같은 사용자면 복호화되지 않나?」는 맞는 말

여기는 얼버무리지 않고 써야 하는 지점입니다.

같은 사용자 권한으로 실행되는 코드는 그 사용자가 복호화할 수 있는 것을 기본적으로 복호화할 수 있습니다.

즉, DPAPI는 다음과 같은 상황을 주 목적으로 하지는 않습니다.

  • 이미 그 단말이 악성코드에 침해되어 있음
  • 공격자가 그 사용자로 코드 실행이 가능함
  • 단말 관리자 레벨에서 완전히 탈취됨

이 상황에서는 앱 자체도 복호화할 수 있으니, 공격자 코드도 복호화해 버립니다.
여기서 「그래도 암호화되어 있어요」는 별로 든든하지 않습니다.

DPAPI가 효과를 발휘하는 것은 주로 “파일 유출·잘못된 배치·오프라인 반출·다른 사용자로부터의 참조” 쪽입니다.

여기를 헷갈리면,

  • 지킬 수 있는 것을 과소평가해서 안 쓴다
  • 지킬 수 없는 것을 과대평가해서 안심한다

양쪽이 다 일어납니다. 둘 다 수수하게 위험합니다.

3.3. 그래서 뭐가 좋은가

DPAPI의 이점을 한 마디로 말하면,

「비밀 그 자체를 설정 파일의 가독성과 분리할 수 있다」

는 점입니다.

예를 들어 다음과 같은 사고에서는 평문과 DPAPI의 차이가 납니다.

  • 사용자가 설정 파일을 지원 측에 보내 버렸다
  • 조사용 ZIP에 설정 파일이 들어갔다
  • 백업에서 설정 파일만 유출됐다
  • 공유 폴더에 복사됐다
  • 개발자가 암호문만 보고 내용을 읽지 못하게 해 두고 싶다

이건 꽤 현실적인 장점입니다.
공격자를 영화 같은 초인으로 만들 필요 없이, 일상 사고의 반경을 작게 만들 수 있습니다.

4. DPAPI가 딱 좋은 이유

Windows에서 로컬 저장 비밀을 다룰 때, DPAPI가 실무에 딱 맞는 이유는 다음과 같습니다.

4.1. 키 관리를 OS에 맡길 수 있다

자체적으로 AES 키를 생성하고 저장하고 권한을 주고 로테이션하고 유출 시 영향을 고민하고 변조 탐지까지 넣는다.
생각보다 묵직합니다. 게다가 대충 하면 대개 같은 곳에 키를 두고 끝납니다.

DPAPI를 쓰면 「암호 키를 어떻게 만들고 어디에 둘 것인가」 문제를 앱 구현에서 떼어낼 수 있습니다.

그런 의미에서 DPAPI는
「암호 알고리즘을 고르는 API」가 아니라, 「키 관리를 OS에 위임하는 API」
로 보는 편이 본질에 가깝습니다.

4.2. 복호화 주체를 Windows 사용자 또는 컴퓨터에 묶을 수 있다

일반 데스크톱 앱이라면 CurrentUser를 고르면 되는 장면이 많습니다.

  • 그 사용자가 로그온해 있을 것
  • 그 사용자 맥락에서 처리가 돌 것

을 전제로 복호화할 수 있습니다.

그 덕에 암호문만 다른 PC로 복사해도 그대로는 쓰기 어려운 성질을 얻을 수 있습니다.

4.3. 변조 탐지까지 포함하기 쉽다

자체 암호화에서 흔한 것은,
「AES로 암호화했으니 끝」이라며 변조 탐지를 잊는 것입니다.

DPAPI는 암호화 데이터에 대한 무결성 보호까지 갖추고 있으므로,
암호문이 몰래 수정됐을 때의 탐지 까지 OS 측 메커니즘에 실어 두기 쉬운, 실무상의 이점이 있습니다.

4.4. C# / .NET에서 자연스럽게 쓸 수 있다

C#이라면 System.Security.Cryptography.ProtectedData를 그대로 쓸 수 있습니다.
쓸데없는 라이브러리를 더하지 않아도 되는 점은 Windows 전용 앱에서는 꽤 도움이 됩니다.

5. DPAPI로 지킬 수 있는 것과 지킬 수 없는 것

여기는 확실히 나눠 두는 편이 안전합니다.

5.1. 지키기 쉬워지는 것

DPAPI는 적어도 다음과 같은 장면에서는 유효합니다.

  • 설정 파일의 평문 유출
  • 다른 PC로의 파일 반출
  • 같은 PC의 다른 사용자로부터의 참조(CurrentUser 전제)
  • 백업이나 첨부 파일로서의 유출
  • 개발·보수 현장에서의 「아차 하고 읽혀버리는」 상태

5.2. 지키지 못하거나 지키기 어려운 것

반대로 다음 상황에서는 과신하지 않는 편이 좋습니다.

  • 같은 사용자 권한으로 실행되는 공격 코드
  • 단말 자체의 완전 침해
  • 관리자 권한에서의 탈취
  • 앱이 복호화한 후의 메모리상 평문
  • 모든 클라이언트에 공통으로 배포되는 장기 비밀

마지막 「전 클라이언트 공통 장기 비밀」은 특히 중요합니다.

예컨대,

  • 모든 고객에게 같은 API 키를 심는다
  • 모든 단말에서 같은 공유 패스워드를 가진다
  • 클라이언트만으로 완결되는 고정 복호화 키를 배포한다

같은 설계는 어느 1대에서 빠진 시점에 전체로 번지기 쉽습니다.

DPAPI는 「그 저장 위치를 평문보다 낫게 하는」 데는 유효하지만,
애초에 클라이언트에 두면 안 되는 비밀을 정당화하는 것은 아닙니다.

6. CurrentUserLocalMachine의 구분

여기는 꽤 중요합니다. 대충 고르면 의미가 달라집니다.

6.1. 기본은 CurrentUser

일반 Windows 데스크톱 앱에서는 먼저 CurrentUser를 기본으로 생각합니다.

적합한 예:

  • WPF / WinForms / WinUI의 사용자용 데스크톱 앱
  • 사용자별로 설정이나 자격 증명을 갖는 앱
  • %LocalAppData%%AppData% 밑에 설정을 두는 앱

이 경우 「그 Windows 사용자의 비밀」 로서 다루기 쉬워집니다.

6.2. LocalMachine은 용도가 꽤 제한적

LocalMachine은 편리해 보이지만 일반 데스크톱 앱에서는 너무 넓습니다.

적합한 건 예컨대 다음 같은 경우입니다.

  • 신뢰된 단일 용도 머신 위의 Windows 서비스
  • 그 머신 위 특정 프로세스에서만 쓰는 기밀
  • 로그온 사용자를 넘어서 같은 단말에서 쓸 필요가 있는 케이스

다만 주의점은 묵직합니다.

  • 그 PC 위에서 도는 프로세스로부터 폭넓게 복호화 가능
  • 공용 단말, RDS, 점프 서버, 여러 사용자가 들어가는 환경에서는 위험해지기 쉽다
  • 「일단 모두가 쓸 수 있으니 편해서」로 고르면 대개 나중에 곤란해진다

6.3. 망설이면 이렇게 생각한다

  • 일반 UI 앱CurrentUser
  • 정말로 머신 단위로 지키고 싶은 특수 케이스LocalMachine
  • 어떤 사용자라도 복호화할 수 있어야 하지만 단말에 다른 사용자도 있다 → 대개 설계부터 다시 보는 편이 좋다

6.4. 서비스나 impersonation에서는 주의가 조금 더 는다

Windows 서비스나 impersonation이 얽히면 CurrentUser의 의미가 조금 무거워집니다.

  • 실행 계정이 누구인가
  • 그 계정의 프로필이 로드되어 있는가
  • 복호화 타이밍이 어느 맥락인가

이쪽이 어긋나면 「암호화는 됐는데 복호화가 안 된다」 가 되기 쉽습니다.
서비스 용도는 「일단 CurrentUser」로 끝나지 않는 경우가 있습니다.

7. 구현의 최소한의 지침

Windows 앱에서 「설정 파일의 평문을 그만둔다」 정도라면 설계를 그렇게 복잡하게 할 필요는 없습니다.
다만 빠뜨리고 싶지 않은 포인트가 몇 가지 있습니다.

7.1. 비밀만 보호한다

설정 전체를 통째로 암호화하기보다 먼저 비밀 항목만 보호하는 편이 다루기 쉽습니다.

예컨대 다음처럼 나눕니다.

  • 서버 URL
  • 사용자명
  • DB명
  • 기능 플래그

는 평문 그대로 둬도 되는 경우가 많습니다.

한편,

  • 패스워드
  • API 토큰
  • 리프레시 토큰
  • 공유 폴더 자격 증명

은 보호 대상입니다.

이렇게 나누면,

  • 설정 편집이 쉽다
  • 차분 확인이 쉽다
  • 어디가 비밀인지 명확
  • 전체 운용이 단순

해집니다.

7.2. 저장 위치는 per-user 기본

일반 데스크톱 앱이라면 저장 위치는 다음과 같은 per-user 위치를 기본으로 합니다.

  • %LocalAppData%\Vendor\App\settings.json
  • %AppData%\Vendor\App\settings.json

적어도 설치 폴더 밑이나 공유되기 쉬운 장소에 대충 두지 않는 편이 좋습니다.

DPAPI로 보호하고 있어도 저장 위치의 ACL이 대충이면,
「암호문은 읽힌다」「설정 구조는 보인다」「운용 실수는 생긴다」
라는 이야기가 됩니다. 방어는 한 겹이 아니라 겹겹이 가는 게 효과적입니다.

7.3. optionalEntropy는 만능의 제2 키가 아니다

ProtectedData에는 optionalEntropy를 넘길 수 있습니다.
편리하지만 “이걸 바이너리에 심어 두면 안전해지는 마법의 제2 키”가 아닙니다.

  • 같은 파일에 두면 비밀이 되지 않습니다
  • 바이너리에 고정값으로 심어도 강한 비밀이라고 할 수 없습니다
  • 그래도 용도 식별이나 오용 방지에는 도움이 됩니다

실무에서는,

  • 앱명
  • 용도명
  • 버전 식별자

를 고정 바이트열로 전달해, 「다른 용도의 암호문을 실수로 받아들이지 않기 위해」 쓰는 정도가 딱 좋습니다.

7.4. 암호문을 Git에 넣어도 된다, 가 아니다

여기도 수수하게 중요합니다.

DPAPI의 암호문은 평문보다 훨씬 낫지만
그렇다고 설정 파일 통째로 리포지토리에 넣어도 된다, 가 아닙니다.

이유는 단순합니다.

  • 암호문은 오래 남는다
  • 언젠가 같은 단말이나 같은 맥락이 재현될지도 모른다
  • 파일에는 비밀 이외의 정보도 들어 있다
  • 「보호되니까 대충 다뤄도 된다」는 문화가 만들어진다

「평문보다 낫다」
「어디에 둬도 안전하다」
는 완전히 다른 이야기입니다.

7.5. 로그에 찍지 않는다

의외로 흔한 것이, 복호화한 후에 로그에 찍어서 다 망가지는 패턴입니다.

  • 접속 실패 시 접속 문자열을 통째로 찍는다
  • API 401 시 Authorization 헤더를 남긴다
  • 예외 메시지에 비밀을 섞는다

이런 걸 하면 설정 파일의 평문을 그만둬도 결국 로그가 평문 창고가 됩니다.
슬프지만 꽤 실무입니다.

8. C# / .NET 최소 구현 예

다음은 설정 파일에 저장할 문자열을 CurrentUser로 보호하는 최소 예입니다.
용도 식별을 위해 고정 optionalEntropy를 넣고 있지만, 이걸 비밀 키라고 생각하지 마세요.

using System;
using System.Security.Cryptography;
using System.Text;

public static class DpapiSecretProtector
{
    // 용도 식별용. 제2 비밀 키가 아님.
    private static readonly byte[] Entropy =
        Encoding.UTF8.GetBytes("ComComponent:DesktopApp:SettingsSecret:v1");

    public static string ProtectToBase64(string plaintext)
    {
        ArgumentNullException.ThrowIfNull(plaintext);

        byte[] plainBytes = Encoding.UTF8.GetBytes(plaintext);
        byte[] protectedBytes = Array.Empty<byte>();

        try
        {
            protectedBytes = ProtectedData.Protect(
                plainBytes,
                optionalEntropy: Entropy,
                scope: DataProtectionScope.CurrentUser);

            return Convert.ToBase64String(protectedBytes);
        }
        finally
        {
            Array.Clear(plainBytes, 0, plainBytes.Length);

            if (protectedBytes.Length > 0)
            {
                Array.Clear(protectedBytes, 0, protectedBytes.Length);
            }
        }
    }

    public static string UnprotectFromBase64(string protectedBase64)
    {
        ArgumentNullException.ThrowIfNull(protectedBase64);

        byte[] protectedBytes = Convert.FromBase64String(protectedBase64);
        byte[] plainBytes = Array.Empty<byte>();

        try
        {
            plainBytes = ProtectedData.Unprotect(
                protectedBytes,
                optionalEntropy: Entropy,
                scope: DataProtectionScope.CurrentUser);

            return Encoding.UTF8.GetString(plainBytes);
        }
        finally
        {
            Array.Clear(protectedBytes, 0, protectedBytes.Length);

            if (plainBytes.Length > 0)
            {
                Array.Clear(plainBytes, 0, plainBytes.Length);
            }
        }
    }
}

사용법은 단순합니다.

string protectedPassword = DpapiSecretProtector.ProtectToBase64(password);

// JSON 등에 저장
// settings.DbPasswordProtected = protectedPassword;

string password = DpapiSecretProtector.UnprotectFromBase64(settings.DbPasswordProtected);

설정 파일은 예컨대 이런 형태로 할 수 있습니다.

{
  "ApiBaseUrl": "https://api.example.com/",
  "UserName": "app-user",
  "PasswordProtected": "AQAAANCMnd8BFdERjHoAwE..."
}

이 형태의 좋은 점은,

  • URL이나 사용자명은 평범하게 편집할 수 있다
  • 패스워드만 보호할 수 있다
  • 설정 구조가 보기 쉽다
  • 평문 그대로 두는 것보다 사고가 덜 난다

는 것입니다.

9. 그래도 위험한 설계

DPAPI를 써도 다음 설계는 여전히 위험합니다.

9.1. 복호화 후의 값을 오래 갖고 다닌다

복호화한 값을,

  • 로그에 찍는다
  • 화면에 띄운다
  • 예외에 포함시킨다
  • 장수명 오브젝트에 계속 매달아 둔다

는 피하고 싶은 것들입니다.

「저장 시 암호화」
「사용 중에도 안전」
은 별개의 문제입니다.

9.2. 전 설치에 공통인 비밀을 심는다

예컨대 전 사용자에게 같은 API 키를 주는 설계는,
DPAPI로 저장해도 근본 해결이 되지 않습니다.

왜냐하면, 어느 1대에서든 앱이 복호화할 수 있다면 그 비밀은 꺼낼 수 있기 때문입니다.

이런 종류의 비밀은,

  • 서버 측에 둔다
  • 클라이언트는 토큰만 갖는다
  • 사용자별 자격 증명으로 한다
  • 기한부 토큰으로 한다

같은 방향으로 옮기는 편이 좋습니다.

9.3. LocalMachine을 「편해서」 고른다

이건 정말 흔합니다.

  • 사용자 전환해도 읽힌다
  • 서비스에서도 읽힌다
  • 돌면 편리하다

는 이유로 LocalMachine을 고르고 싶어집니다.

하지만 일반 데스크톱 앱에서는
「그 PC 위의 다른 프로세스에도 복호화 가능성을 넓힌다」
이므로 의미가 꽤 달라집니다.

9.4. 자체 암호를 더해 안심한다

DPAPI 대신,

  • AES 키를 소스 코드에 심는다
  • AES 키를 설정 파일의 다른 항목에 둔다
  • 「살짝 난독화한 문자열」을 키 취급한다

같은 구현을 넣는 건 대개 효과가 약합니다.

“평문이 아니다”와 “안전하다” 사이에는 꽤 큰 도랑이 있습니다.

10. DPAPI로 부족한 경우

DPAPI는 편리하지만 만능은 아닙니다. 다음 경우에는 다른 선택지를 고려하는 편이 좋습니다.

10.1. Windows 이외에서도 돌리고 싶다

DPAPI / ProtectedData는 Windows용입니다.
크로스 플랫폼 앱이라면 그 전제로는 못 만듭니다.

10.2. 복수 머신·복수 사용자에서 같은 비밀을 다루고 싶다

같은 암호문을 여러 PC에서 복호화하고 싶다, 여러 사용자에서 공용하고 싶다는 요건은,
「그 단말·그 사용자에 묶는」 DPAPI의 특기에서 벗어납니다.

이 경우는,

  • 서버 측의 비밀 관리
  • 자격 증명 기반
  • Windows 인증 / 통합 인증
  • 앱용 자격 증명 저장소

등 요건에 맞는 다른 설계를 생각해야 합니다.

10.3. 저장 대상이 사용자 자격 증명 그 자체

packaged desktop app / WinUI 계열에서 저장 대상이 명확히

  • 사용자명
  • 패스워드

의 조합이라면 Credential Locker도 선택지가 됩니다.
다만 이 글의 중심은 어디까지나 「Windows 클라이언트에서 설정 파일 평문을 그만두기」 위한 DPAPI의 실무 라인입니다.

11. 실무에서의 추천 우선순위

마지막으로, 실무에서 망설이면 이 순서로 생각하면 정리하기 쉽습니다.

우선 1: 애초에 가지지 않는다

  • Windows 인증
  • 통합 인증
  • 대화형 로그인
  • 서버 측에서 비밀 보유
  • 단명 토큰

우선 2: 사용자별 비밀에 맞춘다

  • 공통 비밀보다 per-user
  • 장기 고정 자격 증명보다 갱신 가능한 토큰
  • 전 클라이언트 공통 키를 피한다

우선 3: 로컬 저장이 필요하면 DPAPI

  • 기본은 CurrentUser
  • 저장 위치는 per-user
  • 비밀 항목만 보호
  • 로그에 찍지 않기

우선 4: LocalMachine은 예외 취급

  • 정말로 머신 단위여야 하는가
  • 그 단말에 다른 사용자가 들어오지 않는가
  • 서비스 설계로서 타당한가

12. 정리

Windows 앱에서 설정 파일에 기밀 정보를 저장해야 할 때,
평문 그대로 두는 것은 피하고 싶습니다.

그리고,

「어차피 키는 어딘가에 저장되는 거니까 같은 것 아닌가?」

라는 의문에 대해서는 이렇게 답하는 것이 실무적입니다.

  • 자체 암호화로 키를 같은 장소에 둔다면 꽤 같음
  • DPAPI는 같지 않음
    • 키 관리를 OS에 맡길 수 있다
    • 복호화 주체를 Windows 사용자 / 컴퓨터에 묶을 수 있다
    • 파일 단체의 유출을 그대로 비밀 유출로 만들지 않아도 된다
  • 단,
    • 같은 사용자 권한으로 도는 코드
    • 완전히 침해된 단말
    • 클라이언트에 두면 안 되는 장기 공통 비밀

까지는 해결하지 않는다

요약하면, DPAPI는 만능의 성벽이 아닙니다.
그러나 설정 파일 평문이라는 훤히 뚫린 유리창을, 최소한 제대로 된 창으로 갈아 끼우는 정도의 효과는 있습니다.

Windows 클라이언트의 실무에서는 이 차이가 꽤 큽니다.
우선 여기를 빠뜨리지 않는 것부터 시작하는 게 가장 현실적입니다.

13. 참고 자료

  • 이전 글: https://comcomponent.com/blog/2026/03/14/001-windows-app-security-minimum-checklist/
  • Microsoft Learn: CryptProtectData
    https://learn.microsoft.com/en-us/windows/win32/api/dpapi/nf-dpapi-cryptprotectdata
  • Microsoft Learn: ProtectedData
    https://learn.microsoft.com/en-us/dotnet/api/system.security.cryptography.protecteddata?view=windowsdesktop-10.0
  • Microsoft Learn: DataProtectionScope
    https://learn.microsoft.com/en-us/dotnet/api/system.security.cryptography.dataprotectionscope?view=windowsdesktop-10.0
  • Microsoft Learn: How to: Use Data Protection
    https://learn.microsoft.com/en-us/dotnet/standard/security/how-to-use-data-protection
  • Microsoft Learn: Credential locker for Windows apps
    https://learn.microsoft.com/en-us/windows/apps/develop/security/credential-locker

관련 기사

같은 태그를 공유하는 최신 기사입니다. 더 가까운 주제로 지식을 넓힐 수 있습니다.

관련 토픽

이 기사와 가까운 토픽 페이지입니다. 기사를 출발점 삼아 관련 서비스와 다른 기사로 이어집니다.

이 주제와 연결되는 서비스

이 기사는 다음 서비스 페이지로 이어집니다. 가까운 입구부터 확인해 주세요.

블로그 목록으로 돌아가기