수정 이력(5건, 최종 수정 2026년 09월 03일)
이 글에 적용한 변경 사항의 기록입니다. 보관해 둔 수정 전 버전은 DOI가 부여된 고정 URL에서 읽을 수 있습니다.
- Codex 리뷰에 따라 상담·문의 링크에 /ko/ 로케일 접두를 붙였습니다. 본문의 기술적인 주장은 바꾸지 않았습니다.
- permalink·저자 표기·지식 맵 래퍼·깨진 내부 링크 등 CI가 지적한 표시용 수정을 반영했습니다. 본문의 기술적인 주장은 바꾸지 않았습니다.
- 기사 앞부분에 「이 기사의 지식 맵」 절을 추가했습니다. 본문에서 다루는 개념과 그 관계를 요약·그림·상세 페이지 링크로 정리한 것입니다. 본문의 주장은 바꾸지 않았습니다.
- 외부 리뷰(1283건)에 대응하여 본문을 업데이트했습니다. 개별 변경 내용은 아래 이력을 참조하세요.
- gMSA 도입 절차의 골격(KDS 루트 키, `New-ADServiceAccount`, `Install-`와 `Test-ADServiceAccount`, 전제 조건, 이벤트 ID로 확인)을 추가했습니다. DPAPI가 「동일 사용자·동일 머신」에서만 복호화되는 이유를 본문에서 완결하고, 작업의 실행 계정으로 자격 정보를 저장하는 구체 절차, 관리 ID로의 연결, Protected Event Logging이 사용 중인지 확인하는 방법을 보강했습니다. 작업 스케줄러에 gMSA를 등록하는 절차는 지정해야 할 값을 1차 정보로 확인할 수 없어 조사할 곳만 제시하는 데 그칩니다.
- 최초 공개
이 글을 인용하기(DOI(등록된 아카이브): 10.5281/zenodo.22174781)
아래 DOI는 이전에 등록된 아카이브를 가리키며 현재 본문과 다를 수 있습니다. 현재 본문을 참조할 때는 이 페이지의 URL을 사용하세요.
Go Komura (2026). 「PowerShell에서 자격 정보를 안전하게 다루기 ── 스크립트에서 평문 비밀번호를 없애기」. 합동회사 코무라소프트. https://comcomponent.com/ko/blog/powershell-credential-secretmanagement/
- DOI(등록된 아카이브)
- 10.5281/zenodo.22174781
- DOI(마지막 등록 버전)
- 10.5281/zenodo.22174782
장애 조사나 스크립트 리뷰에서 고객의 PowerShell 스크립트를 보면, 상당히 높은 확률로 마주치는 것이 있습니다. $password = "P@ssw0rd123"── 파일 서버 연결, 기간 시스템의 DB, 메일 발송, Web API 키. 일단 동작시키는 것을 우선한 결과, 비밀번호가 평문으로 스크립트에 박힌 채 공유 폴더나 Git 리포지토리에서 수년간 살아 남습니다.
까다로운 점은, 작성한 본인도 「좋지 않다는 것은 안다」는 것입니다. 그렇다면 대신 무엇이 정답인가 하면, SecureString, Export-Clixml, SecretManagement, 자격 정보 관리자, Azure Key Vault…로 선택지가 난립해 각각의 적용 범위와 한계가 잘 보이지 않습니다. 게다가 「SecureString은 이제 권장되지 않는다」는 이야기까지 들려와, 무엇을 믿어야 할지 헷갈립니다. 이 혼란에는 이유가 있고, 정리하면 경로는 명확합니다.
이 기사에서는 사내 운영 스크립트나 정기 배치를 관리하는 정보시스템·운영 담당자를 대상으로, 평문 비밀번호가 무엇이 문제인지부터 시작해 PowerShell 자격 정보 도구를 메커니즘 단위로 정리하고, 「무인 실행에서 어떻게 할 것인가」의 현실적인 답을 판단표로 정리합니다. PowerShell 7.x를 기준으로 하며, Windows PowerShell 5.1 현장에 대한 주의도 함께 적습니다.
1. 먼저 결론
- 평문 비밀번호의 문제는 「파일을 본 시점에 유출이 확정」된다는 점입니다. Git 이력·공유 폴더·백업·로그와, 스크립트 복본이 늘어나는 경로가 모두 유출 경로가 됩니다. 평문을 SecureString으로 변환하는 코드(ConvertTo-SecureString -AsPlainText)를 써도, 원래 평문이 스크립트나 로그에 남으면 해결이 되지 않습니다. 12
- 대화형으로 쓰는 스크립트는 Get-Credential으로 PSCredential을 받는 것이 기본형입니다. 비밀번호는 화면에 표시되지 않고, 객체로서 각 명령의 -Credential 매개변수로 넘길 수 있습니다. 3
- SecureString은 「신규 개발에서는 권장하지 않는다」고 .NET 공식이 명시합니다. 암호화는 Windows에서만 이루어지며, 비 Windows에서는 내부가 암호화되지 않습니다. 한편 PowerShell은 호환성을 위해 SecureString을 계속 쓰고 있으며, 표준 전달 형식으로 당분간은 함께 가게 됩니다. 과신하지 말고, 자체 보호 메커니즘의 토대로는 쓰지 마세요. 45
- 무인 실행에서 자격 정보 파일을 저장할 때는 Export-Clixml에 의한 DPAPI 암호화가 최소 구성의 실용적 해법입니다. 저장한 사용자·저장한 머신에서만 복호화되므로, 파일만 유출되어도 열리지 않습니다. 뒤집으면 「작업 스케줄러의 실행 계정 자신이 저장해야」 합니다. 비 Windows에서는 암호화되지 않습니다. 6
- 여러 비밀을 다룰 때는 SecretManagement+SecretStore로 일원 관리합니다. Set-Secret/Get-Secret의 통일 인터페이스로, 로컬 SecretStore부터 Azure Key Vault까지 저장 위치를 갈아끼울 수 있습니다. 다만 무인 실행에서는 볼트 비밀번호 취급이 과제로 남습니다. 78
- 최우선 검토는 「애초에 자격 정보를 갖지 않는 설계」입니다. 실행 계정(도메인 계정이나 gMSA) 자체에 연결 대상 권한을 주면, 스크립트에서 비밀번호가 사라집니다. gMSA라면 비밀번호 관리 자체를 OS에 맡길 수 있습니다. 910
- 로그와 트랜스크립트로의 유출을 설계에 포함합니다. 스크립트 블록 로그를 켜면, 스크립트에서 쓴 자격 정보 등 민감 데이터가 이벤트 로그에 기록될 수 있다고 공식 문서가 경고합니다. 평문 비밀번호를 커맨드라인에 친 시점에, 기록에 남는다는 전제로 생각합니다. 11
그림의 실선은 항상 성립하는 관계, 점선은 조건이 붙는 관계입니다(성립 조건은 상세 페이지의 관계별 설명에 적혀 있습니다). 관계 전체 목록(총 24건, 근거와 확신도 포함)과 주요 개념의 정의는 지식 맵 상세 페이지에 정리되어 있습니다(일본어). 데이터: JSON-LD / Turtle
2. 평문이 무엇이 문제인가 ── 유출 경로는 복본 수만큼 있다
먼저, 고쳐야 할 전형적인 패턴을 확인해 둡니다.
# 안티패턴: 둘 다 「평문이 스크립트에 남는다」는 점에서 같다
$password = "P@ssw0rd123"
# SecureString으로 변환해도, 원래 평문은 1행에 적힌 채로 남는다.
# PSScriptAnalyzer는 이 -AsPlainText 사용을 오류로 검출한다
$secure = ConvertTo-SecureString $password -AsPlainText -Force
$cred = New-Object System.Management.Automation.PSCredential('svc-transfer', $secure)
비밀번호를 직접 적는 것이 위험한 이유는, 「악의적 침입자에게 읽힐지도 모른다」만이 아닙니다. 스크립트라는 산출물의 성질상, 정상적인 운영만으로도 복본이 증식한다는 점입니다.
| 복본이 생기는 곳 | 무슨 일이 일어나는가 |
|---|---|
| Git 리포지토리 | 한 번 커밋한 비밀번호는 이력에 영구 보존. 나중에 파일을 고쳐도 이력에는 남는다 |
| 공유 폴더 | 읽기 권한이 있는 전원+백업+세대 복본으로 확산 |
| 메일·채팅 | 「이 스크립트 써 봐」라고 첨부한 순간에 통제 불능 |
| 이벤트 로그 | 스크립트 블록 로그가 켜져 있으면, 실행된 코드 내용이 로그에 기록된다11 |
| 트랜스크립트 | Start-Transcript나 조직의 트랜스크립션 설정이, 화면에 흐른 내용을 기록한다11 |
이 구조가 의미하는 것은, 평문 비밀번호 대책은 「파일을 보지 못하게 하는 것」으로는 달성할 수 없다는 점입니다. 액세스 권한을 아무리 좁혀도 Git 이력과 백업까지는 막을 수 없습니다. 비밀번호를 스크립트 밖으로, 암호화된 보관 장소로 빼내는 것이 본류입니다. 이 생각은 .NET 앱 설정 파일에 대해 「Windows 앱의 비밀 정보 저장 - DPAPI로 평문 설정을 피하기」에서 쓴 것과 같고, PowerShell에서도 원칙은 바뀌지 않습니다.
참고로, 평문을 그 자리에서 SecureString으로 변환하는 ConvertTo-SecureString "P@ssw0rd" -AsPlainText -Force는 해결책이 아닙니다. PSScriptAnalyzer(공식 정적 분석 도구)는 이 쓰기를 오류(Severity: Error)로 검출합니다. 암호화를 우회해 평문을 메모리에 노출하는 데다가, 원래 평문이 스크립트 안에 계속 남기 때문입니다. 1
3. 도구의 실체 ── PSCredential·SecureString·그 한계
3.1. Get-Credential과 PSCredential
대화형 스크립트의 기본형은 이것뿐입니다.
# 사용자 이름과 비밀번호 입력을 요청하고, PSCredential 객체로 받는다
$cred = Get-Credential -Message '기간 DB의 연결 계정을 입력하세요'
# -Credential 매개변수를 가진 명령에 그대로 넘길 수 있다
Invoke-Command -ComputerName APPSV01 -Credential $cred -ScriptBlock { hostname }
Get-Credential은 사용자 이름과 비밀번호 입력을 재촉하고 PSCredential 객체를 반환합니다. Windows PowerShell 5.1에서는 대화 상자, PowerShell 6 이후에는 콘솔 입력이 됩니다. 3 비밀번호는 PSCredential 안에서 SecureString으로 유지되며 화면에는 표시되지 않습니다. 「사람이 그 자리에서 입력하는」 운영이 허용되는 장면에서는, 이보다 복잡한 일을 할 필요는 없습니다.
자체 함수를 만드는 쪽은 비밀번호를 [string]으로 받지 말고, [PSCredential]의 -Credential 매개변수를 받도록 설계합니다. 쓰기 상세는 공식 해설 「Add Credential support to PowerShell functions」에 정리되어 있습니다. 2
3.2. SecureString의 실체 ── 「비권장」을 올바르게 받아들이기
SecureString에는 알아 둘 사실이 세 가지 있습니다.
- .NET 공식은 「신규 개발에서는 쓰지 말 것을 권장」합니다. 내부 배열의 암호화는 Windows에서만 이루어지며, 비 Windows 플랫폼에서는 내부 스토리지가 암호화되지 않습니다. 또한 쓰는 순간에는 결국 평문 표현으로 변환되므로, 노출 시간을 짧게 하는 효과만 있습니다. 권장 대체는 「프로세스 밖에 저장된 자격 정보에 대한 불투명한 핸들」, 즉 OS의 자격 정보 저장소나 Key Vault 같은 메커니즘입니다. 4
- 그래도 PowerShell의 표준 장비는 SecureString 전제입니다. PowerShell은 호환성을 위해 SecureString을 계속 지원하며, 콘솔이나 로그에 우연히 노출되는 것을 피하는 용도로 지금도 쓰입니다. 평문 문자열보다는 안전하다는 위치입니다. 5
- 쉽게 평문으로 되돌릴 수 있습니다. PowerShell 7에서는
ConvertFrom-SecureString -AsPlainText한 방으로 평문이 됩니다. 12 SecureString은 「읽을 수 없는 금고」가 아니라 「무심코 보이지 않게 하는 봉투」 정도로 생각하는 것이 실체에 맞습니다.
실무의 결론은 이렇습니다. SecureString을 이유로 복잡한 자체 암호화로 달리지 마세요. PowerShell 도구(Get-Credential, SecretManagement)가 요구하는 형식으로 쓰고, 보호의 본체는 DPAPI나 볼트처럼 프로세스 밖의 메커니즘에 둡니다.
4. 무인 실행의 정석 ── Export-Clixml의 DPAPI 저장과 「동일 사용자·동일 머신」의 벽
작업 스케줄러로 도는 무인 스크립트는 Get-Credential으로 사람에게 물을 수 없습니다. 최소 구성의 실용적 해법이 Export-Clixml에 의한 자격 정보 파일입니다.
# --- 준비(한 번만, 작업의 실행 계정으로 실행한다) ---
$cred = Get-Credential -Message '연동용 계정'
$cred | Export-Clixml -Path 'D:\Jobs\secrets\transfer.credential'
# --- 프로덕션 스크립트(무인 실행) ---
$cred = Import-Clixml -Path 'D:\Jobs\secrets\transfer.credential'
# 예: 별도 자격 정보가 필요한 공유 연결이나 원격 실행에 쓴다
Invoke-Command -ComputerName FILESV01 -Credential $cred -ScriptBlock { Get-ChildItem D:\Export }
Export-Clixml은 자격 정보 객체를 Windows의 DPAPI(데이터 보호 API)로 암호화해 저장합니다. 복호화할 수 있는 것은, 저장한 사용자 계정이 저장한 그 컴퓨터에서 열었을 때뿐입니다. 내보낸 파일은 다른 머신이나 다른 사용자에서는 쓸 수 없습니다. 613 파일이 반출되어도 열리지 않는다는 성질이 무인 실행의 저장 위치로 잘 맞습니다.
왜 그렇게 되는지는, 메커니즘을 한 단만 들여다보면 납득됩니다. DPAPI는 사용자의 로그온 자격 정보(보통 비밀번호 해시)에서 이끌어 낸 키로 「마스터 키」를 암호화하고, 그것을 그 사용자 프로필 안에 둡니다. 데이터 본체는 그 마스터 키에서 만들어지는 세션 키로 암호화됩니다. 즉 키를 쥐고 있는 것은 「그 사용자로 로그온할 수 있다」는 것 자체이며, 키 파일을 따로 어딘가에 보관할 필요는 없는 대신 키가 사용자와 머신에 묶입니다. 공식 문서도 「보통 암호화한 사용자와 같은 로그온 자격 정보를 가진 사용자만 복호화할 수 있고, 암호화와 복호화는 같은 컴퓨터에서 수행해야 한다」고 설명합니다. 14 이것이 「동일 사용자·동일 머신 한정」의 내용입니다.
다만 이 「동일 사용자·동일 머신 한정」은 보호인 동시에 운영의 함정입니다.
- 작업 스케줄러의 실행 계정과 맞춰야 합니다. 자신의 계정으로 만든 .credential 파일은, 작업이 서비스 계정으로 도는 순간에 복호화할 수 없게 됩니다. 준비 작업은 반드시 「작업의 실행 계정으로서」 수행합니다(그 계정으로 PowerShell을 시작해 저장). 작업의 실행 계정과 로그온 종류에 대한 생각은 「작업 스케줄러의 작업이 실행되지 않음·0x1로 끝남」에서 자세히 다룹니다.
- 서버 교체·계정 변경 시에는 반드시 다시 만들어야 합니다. 「이전했더니 안 된다」의 단골 원인이므로, 준비 절차를 스크립트화해 리포지토리에 남겨 둡니다(비밀번호 자체는 물론 넣지 않습니다).
- 동일 계정으로 동작하는 프로세스에서는 복호화할 수 있습니다. DPAPI는 그 사용자로 도는 코드에는 열려 있으므로, 실행 계정에 불필요한 소프트웨어를 함께 두지 않고, 계정 권한을 최소로 하는 주변 위생 관리는 계속 필요합니다.
- 비 Windows에서는 암호화되지 않습니다. macOS/Linux에서는 사실상 평문(Unicode 문자 배열)으로 출력된다고 공식 문서에 명시되어 있습니다. 6
「그 계정으로 PowerShell을 시작해 저장한다」의 구체 절차도 적어 둡니다. 위의 준비 부분(Get-Credential과 Export-Clixml 두 줄)을 D:\Jobs\save-credential.ps1로 저장해 두고, 그 계정으로 PowerShell을 시작해 실행합니다.
# 준비를 작업의 실행 계정으로 수행: runas로 그 계정으로서 PowerShell을 시작한다
# 비밀번호는 runas가 대화형으로 물으므로, 커맨드라인·이력·트랜스크립트에 평문이 남지 않는다
runas /user:CONTOSO\svc-transfer "pwsh.exe -NoProfile -NoExit -File D:\Jobs\save-credential.ps1"
runas는 대화형 로그온에 해당하는 권한을 쓰므로, 그 계정에 「로컬 로그온」을 허용하지 않은 환경에서는 실패합니다. 그 경우에는 준비 작업 동안에만 로그온을 허용하고, 저장이 끝나면 원래 설정으로 되돌리는 것이 현실적입니다. Get-Credential 입력 자체에 대화형 세션이 필요하므로, 「무인 작업을 하나 만들어 준비한다」는 방법은 쓸 수 없습니다. 또한 실행 계정의 프로필이 한 번도 만들어지지 않은 머신에서는, 최초 로그온으로 프로필이 만들어진 뒤에 DPAPI 마스터 키가 놓인다는 점도 이 절차를 밟는 이유 중 하나입니다.
참고로 ConvertFrom-SecureString에 -Key를 지정해 AES 암호화하는 방법도 있지만, 이번에는 「키 파일을 어떻게 지킬 것인가」라는 같은 문제가 한 단 밀려 남을 뿐입니다. 12 머신을 넘어야 하는 시점이 오면, 다음 장의 볼트나 제6장의 「갖지 않는 설계」로 나아가야 합니다.
5. 비밀이 늘어나면 ── SecretManagement과 SecretStore
연결 대상이 늘고 API 키나 토큰도 섞이기 시작하면, .credential 파일이 흩어져 관리 한계에 닿습니다. 그때 쓰는 것이 SecretManagement 모듈입니다. 비밀 보관 장소(볼트)에 대한 통일 인터페이스이며, 실제 저장은 확장 볼트가 맡습니다. 로컬 저장용 SecretStore(Microsoft 제작) 외에 Azure Key Vault, KeePass 등의 확장 볼트를 같은 명령으로 다룰 수 있습니다. 715
# 최초 한 번만: 모듈 도입과 볼트 등록
Install-Module Microsoft.PowerShell.SecretManagement, Microsoft.PowerShell.SecretStore
Register-SecretVault -Name SecretStore -ModuleName Microsoft.PowerShell.SecretStore -DefaultVault
# 비밀 등록(최초 액세스 시 볼트 비밀번호를 설정한다)
Set-Secret -Name TransferJobCred -Secret (Get-Credential)
# 스크립트 측: 이름으로 꺼낸다. 저장 위치가 바뀌어도 이 행은 바뀌지 않는다
$cred = Get-Secret -Name TransferJobCred
비밀 값에는 PSCredential이나 SecureString 외에 문자열·바이트 배열·해시 테이블도 저장할 수 있으며, 최초 액세스 시 볼트 자체를 보호하는 비밀번호 설정을 요구받습니다. 16 이점은 스크립트에서 「어디에 저장되어 있는가」라는 지식이 사라진다는 점입니다. 개발 머신에서는 SecretStore, 프로덕션에서는 Azure Key Vault(Az.KeyVault 모듈이 확장 볼트를 제공)라는 갈아끼우기가 Register-SecretVault 구성 변경만으로 끝납니다. 177
한편 무인 실행에 가져올 때의 주의가 세 가지 있습니다.
- SecretStore는 기본적으로 대화형으로 볼트 비밀번호를 요구합니다. 그대로 작업 스케줄러에 올리면 프롬프트 대기에서 멈춥니다. 공식 문서는 무인 실행용으로 Interaction을
None으로 하고, DPAPI 보호 파일(Export-Clixml)에 빼 둔 볼트 비밀번호를 Unlock-SecretStore로 넘기는 구성을 안내합니다. 8 즉 볼트의 키는 결국 DPAPI로 지키게 되며, 앞 장의 동일 사용자·동일 머신 제약을 이어받습니다. 비밀번호 요구 자체를 무효화(Authentication None)하는 구성도 가능하지만, 키가 파일 시스템 권한만으로 지켜지게 되므로 강한 보호가 필요한 용도에서는 권장되지 않습니다. 18 - gMSA 같은 관리되는 계정에서는 동작하지 않습니다. SecretManagement은 프로필($env:LOCALAPPDATA)과 DPAPI에 의존하며, 프로필이 없는 Windows의 관리되는 계정(managed accounts)을 현재 지원하지 않는다고 명시되어 있습니다. 15 gMSA로 도는 작업에 비밀을 두고 싶다면 이 조합은 고를 수 없습니다(애초에 gMSA라면 비밀을 갖지 않는 설계로 모을 수 있는 경우가 많습니다 ── 다음 장).
- SecretManagement/SecretStore는 기능 완성(feature complete) 취급이며, 적극적인 신기능 개발은 종료되었습니다. 보안 수정과 중대 버그 대응은 계속되므로 쓰는 것 자체는 문제 없지만, 공식도 「비밀의 성격은 비밀번호 없음이나 연계 자격 정보 쪽으로 바뀌고 있다」고 하며, 장기 설계에서는 인증 방식 자체의 재검토(Entra ID의 관리 ID 등)도 시야에 넣어야 합니다. 7 관리 ID는 Azure상의 리소스에 할당한 ID로 토큰을 받아, 비밀번호나 키를 갖지 않고 Entra ID 대응 서비스에 인증하는 메커니즘입니다. 다음에 무엇을 읽을까라는 의미에서는, 먼저 개요 페이지에서 시스템 할당과 사용자 할당의 차이를 잡고, 19 PowerShell에서 쓴다면 Az 모듈의
Connect-AzAccount -Identity(관리 ID로 로그인)를 입구로 하면 헤매지 않습니다.
Windows의 자격 정보 관리자(Credential Manager)를 볼트로 쓰는 커뮤니티 확장도 있습니다. 7 cmdkey로 등록한 자격 정보가 자동으로 쓰이는 메커니즘 등, 자격 정보 관리자 자체의 동작은 「네트워크 드라이브와 UNC 경로의 함정」에서 다룬 대로이며, 이쪽도 사용자 프로필 단위라는 점은 같습니다.
6. 최우선의 현실적 해법 ── 애초에 자격 정보를 갖지 않기
여기까지 「어떻게 안전하게 저장할 것인가」를 쌓아 왔지만, 사실 무인 실행에서 가장 깨끗한 답은 저장 방법을 다듬는 것이 아닙니다. 스크립트가 자격 정보를 가질 필요를 없애는 것입니다.
Windows의 작업이나 서비스는 실행 계정의 보안 컨텍스트에서 동작합니다. 연결 대상이 Windows 인증으로 지켜져 있다면 ── 공유 폴더의 ACL, SQL Server의 Windows 인증, 사내 API의 Windows 통합 인증 ── 실행 계정 자신에 연결 대상 권한을 주면, 스크립트에는 비밀번호도 Get-Secret도 전혀 등장하지 않습니다. 인증은 Kerberos가 실행 계정의 신원으로 알아서 해 줍니다.
이 구성을 지탱하는 것이 gMSA(그룹 관리 서비스 계정)입니다. gMSA는 도메인의 관리되는 계정이며, 비밀번호 관리를 Windows OS가 맡습니다. 9 비밀번호는 240바이트 난수 생성이고 30일마다 OS가 자동 변경하므로, 사람은 아무도 비밀번호를 모르며 만료로 인한 중지도 일어나지 않습니다. 10 그리고 gMSA는 Windows 서비스뿐 아니라 작업 스케줄러의 작업에도 쓸 수 있습니다. 20
gMSA 도입 절차의 골격도 적어 둡니다. 「쓸 수 있다」로 끝나면 다음에 무엇을 조사해야 할지 모르기 때문입니다. 전제로서 도메인·포리스트 기능 수준이 Windows Server 2012 이상일 것, 작업 계정이 Domain Admins 상당일 것, 도메인 컨트롤러가 아닌 곳에서 작업한다면 RSAT(ActiveDirectory 모듈)가 들어가 있을 것이 필요합니다. 20
# 1) 도메인에 하나만: KDS 루트 키를 만든다(이미 있으면 이 절차는 불필요)
# 모든 도메인 컨트롤러로의 복제를 기다리므로, 실제로 gMSA를 만들 수 있기까지 최대 10시간 걸린다
Add-KdsRootKey -EffectiveImmediately
# 2) gMSA를 만든다. PrincipalsAllowedToRetrieveManagedPassword에는,
# 비밀번호 취득을 허용할 컴퓨터(를 모은 보안 그룹)를 지정한다
New-ADServiceAccount -Name svc-transfer -DNSHostName svc-transfer.contoso.local `
-PrincipalsAllowedToRetrieveManagedPassword 'gMSA-Hosts'
# 3) 실제로 작업을 돌릴 서버 측에서: gMSA를 설치하고, 비밀번호를 받을 수 있는지 확인한다
Install-ADServiceAccount -Identity svc-transfer
Test-ADServiceAccount -Identity svc-transfer
KDS 루트 키 작성은 도메인에서 한 번만 하는 작업입니다. 작성 직후에는 복제 사정으로 gMSA를 만들 수 없으므로, 검증 환경에서 기다리기 싫을 때 -EffectiveTime ((Get-Date).AddHours(-10))으로 과거 시각을 지정하는 절차가 공식에 안내되어 있습니다(프로덕션에서는 쓰지 마세요). 21 작성 후에는 KDS 서비스의 운영 로그에 이벤트 ID 4004가 기록되어 있는지로 확인할 수 있습니다. 20
작업에 할당할 때는 작업의 실행 계정에 gMSA(CONTOSO\svc-transfer$처럼 끝이 $인 계정 이름)를 지정합니다. gMSA는 사람이 비밀번호를 모르므로, 「실행 계정의 비밀번호를 입력한다」는 전제의 GUI 절차와는 흐름이 다릅니다. 실무에서는 ScheduledTasks 모듈의 New-ScheduledTaskPrincipal과 Register-ScheduledTask로 등록하게 되므로, 이 두 cmdlet의 문서를 다음 조사처로 삼으세요. 아울러 그 서버에서 gMSA에 「배치 작업으로 로그온」 권한을 주는 설정이 필요할 수 있습니다.
판단의 우선순위를 정리하면 이렇습니다.
- 연결 대상을 Windows 인증으로 할 수 있다면, 실행 계정(도메인 계정/gMSA)에 권한을 부여해 자격 정보를 없앤다. 도메인 환경의 무인 작업은 먼저 이것을 검토합니다. 9
- 그것이 안 되는 상대(SQL 인증 DB, API 키, 작업 그룹 NAS 등)만 비밀을 저장한다. 단건이면 Export-Clixml의 DPAPI 저장, 여러 개면 SecretManagement+SecretStore. 68
- 클라우드 자원 연결은 키 저장보다 관리 ID/페더레이션 자격 정보 이용을 우선한다. 저장할 때도 Azure Key Vault 같은 전용 볼트로. 177
원격 서버에 자격 정보를 명시적으로 넘겨 처리를 실행하는 장면에서는 PowerShell Remoting의 인증 메커니즘도 얽힙니다. 동시에 공개한 「PowerShell Remoting/WinRM 입문」을 참조하세요.
7. 로그·트랜스크립트에 비밀을 남기지 않기
저장을 단단히 해도, 실행 시 기록에서 새면 의미가 없습니다. 잡아 둘 점은 두 가지입니다.
첫째, PowerShell은 실행 내용을 기록하는 기능을 여러 개 갖고 있으며, 조직 설정으로 켜져 있는 경우가 있습니다. 스크립트 블록 로그를 켜면, 처리한 모든 스크립트 블록의 내용이 이벤트 로그에 기록됩니다. 공식 문서는 「스크립트 로그를 켜면, 스크립트에서 쓰인 자격 정보나 민감 데이터가 이벤트 로그에 기록될 수 있다」고 명시적으로 경고하며, 진단 이외의 용도에서는 보호된 이벤트 로그(Protected Event Logging) 병용을 권장합니다. 11 보호된 이벤트 로그는 대상 이벤트 로그의 내용을 CMS(암호 메시지 구문)의 공개 키로 암호화해 쓰고, 비밀 키를 가진 별도의 안전한 장소에서만 복호화할 수 있게 하는 메커니즘입니다. 11 트랜스크립션(조작 받아쓰기)도 마찬가지로, 콘솔에 흐른 내용은 파일에 남습니다.
자기 환경이 어떤지는 정책이 쓰는 레지스트리 값으로 확인할 수 있습니다. 보호된 이벤트 로그는 그룹 정책의 「컴퓨터 구성 > 관리 템플릿 > Windows 구성 요소 > 이벤트 로깅(Event Logging)」에 있는 「보호된 이벤트 로그 사용(Enable Protected Event Logging)」으로 설정되며, 대응하는 레지스트리는 다음과 같습니다. 22
# 보호된 이벤트 로그가 사용 중인지(정책 미구성이면 키 자체가 없다)
Get-ItemProperty -Path 'HKLM:\SOFTWARE\Policies\Microsoft\Windows\EventLog\ProtectedEventLogging' `
-Name EnableProtectedEventLogging -ErrorAction SilentlyContinue
# 스크립트 블록 로그 측 정책(PowerShell 7계)
Get-ItemProperty -Path 'HKLM:\SOFTWARE\Policies\Microsoft\PowerShellCore\ScriptBlockLogging' `
-Name EnableScriptBlockLogging -ErrorAction SilentlyContinue
# Windows PowerShell 5.1 측은 다른 키
Get-ItemProperty -Path 'HKLM:\SOFTWARE\Policies\Microsoft\Windows\PowerShell\ScriptBlockLogging' `
-Name EnableScriptBlockLogging -ErrorAction SilentlyContinue
스크립트 블록 로그의 레지스트리 값은 PowerShell 7계가 PowerShellCore 아래11, Windows PowerShell 5.1이 Windows\PowerShell 아래23로 나뉩니다. 5.1과 7이 공존하는 서버에서는 양쪽을 확인하세요.
확인의 요점은, 스크립트 블록 로그만 켜져 있고 보호된 이벤트 로그는 꺼져 있는 조합입니다. 이는 「기록은 하되 평문으로 쌓는」 상태이므로, 자격 정보를 다루는 스크립트를 올리기 전에 파악해 두세요.
둘째, 그래서 「평문이 커맨드라인이나 화면을 지나가지 않는」 쓰기를 철저히 합니다.
- 비밀번호를 인수로 받는 설계를 하지 마세요.
.\job.ps1 -Password "P@ssw0rd"라고 친 시점에 로그·이력·프로세스 목록에 남는다는 전제로 생각합니다. 받는다면 PSCredential이나 SecureString으로 받습니다. 2
# 자체 스크립트 입구의 관례: 비밀번호를 [string]으로 받지 않는다
[CmdletBinding()]
param(
# 자격 정보는 PSCredential으로 받는다. 생략 시 Get-Credential으로 대화형 취득,
# 무인 실행에서는 Import-Clixml이나 Get-Secret 결과를 넘긴다
[Parameter(Mandatory)]
[System.Management.Automation.PSCredential]$Credential
)
# 비밀을 포함한 변수는 디버그 출력하지 않는다. 내는 것은 사용자 이름까지
Write-Verbose "연결 계정: $($Credential.UserName)"
- 비밀을 포함한 변수를 Write-Host나 Write-Verbose로 디버그 출력하지 마세요. 문제 해결 때의 「일시적인 셈」인 출력이 트랜스크립트에 영속화됩니다.
- 예외 메시지로의 혼입에 주의하세요. 연결 문자열을 조립한 뒤에 오류를 던지면, catch한 로그에 연결 문자열까지 기록되기 쉽습니다. 오류 처리에서 로그에 무엇을 쓸지의 설계는 「PowerShell의 오류 처리와 재실행 설계」도 함께 보세요.
8. 실무의 정석(판단표)
| 상황 | 권장 | 판단의 눈금 |
|---|---|---|
| 사람이 그 자리에서 실행한다 | Get-Credential | 저장하지 않는 것이 가장 안전. PSCredential으로 받아 -Credential로 넘긴다3 |
| 도메인 내 무인 작업(연결 대상이 Windows 인증) | 실행 계정/gMSA에 권한 부여 | 자격 정보를 갖지 않는 설계를 최우선. gMSA는 작업 스케줄러에서도 쓸 수 있다920 |
| 무인 작업에서 비밀이 1〜2개 | Export-Clixml의 DPAPI 저장 | 작업의 실행 계정 자신이 저장. 머신·계정 변경 시 다시 만든다6 |
| 무인 작업에서 비밀이 다수·저장 위치를 나중에 갈아끼우고 싶다 | SecretManagement+SecretStore | 볼트 비밀번호는 DPAPI 저장+Unlock-SecretStore로 공급. gMSA에서는 쓸 수 없다815 |
| 클라우드 자원·여러 서버에서 공유하는 비밀 | Azure Key Vault(+SecretManagement) | 머신 국소의 DPAPI로는 공유할 수 없다. 집중 관리와 감사가 필요해지면17 |
| 스크립트 안의 평문+ConvertTo-SecureString | 고쳐야 할 대상 | PSScriptAnalyzer가 오류로 판정하는 나쁜 형태. 위의 어느 쪽으로 이전한다1 |
망설이면 「갖지 않기 > 머신에 고정해 갖기(DPAPI) > 볼트로 갖기」 순으로, 위에서부터 검토하세요.
9. 정리
- 평문 비밀번호의 본질적 문제는 Git 이력·공유 폴더·로그라는 복본 증식 경로가 모두 유출 경로가 된다는 점입니다. 액세스 권한 관리로는 지키지 못합니다.
- 대화형 실행은 Get-Credential+PSCredential으로 충분합니다. 비밀번호를 [string]으로 받는 자체 함수는 만들지 마세요.
- SecureString은 .NET 공식이 신규 개발에서 비권장으로 두는 한편, PowerShell의 표준 전달 형식으로 남아 있습니다. 「무심코 노출을 막는 봉투」로 보고, 보호의 본체는 외부 메커니즘에 둡니다.
- Export-Clixml의 DPAPI 저장은 「동일 사용자·동일 머신 한정」입니다. 작업 스케줄러의 실행 계정 자신이 저장 파일을 만드는 것이 요점이며, 비 Windows에서는 암호화되지 않습니다.
- SecretManagement+SecretStore는 여러 비밀의 일원 관리에 유효하지만, 무인 실행에서는 볼트 비밀번호 공급 설계가 필요하고 gMSA에서는 쓸 수 없습니다. 기능 완성 취급이라는 점도 감안해 채택합니다.
- 최우선은 자격 정보를 갖지 않는 설계입니다. Windows 인증+실행 계정(gMSA)에 권한 부여로 스크립트에서 비밀번호를 지울 수 없는지 먼저 검토하세요.
- 스크립트 블록 로그나 트랜스크립트에는 민감 데이터가 남을 수 있습니다. 평문이 커맨드라인·화면·예외 메시지를 지나가지 않는 쓰기를 철저히 합니다.
관련 기사
- Windows 앱의 비밀 정보 저장 - DPAPI로 평문 설정을 피하기
- 작업 스케줄러의 작업이 실행되지 않음·0x1로 끝남 ── 원인 분리와 안전한 운영 설계
- 네트워크 드라이브와 UNC 경로의 함정 ── 업무 앱에서 파일 서버(공유 폴더)를 다루는 실무
- PowerShell Remoting/WinRM 입문
- PowerShell의 오류 처리와 재실행 설계
- PowerShell 스크립트의 인수 설계와 모듈화 ── 「동작하는 스크립트」에서 「남에게 넘길 수 있는 스크립트」로
관련 상담 영역
合同会社小村ソフト에서는 운영 스크립트·정기 배치에 박힌 자격 정보의 전수 조사와 안전한 보관으로의 이전, gMSA를 포함한 실행 계정 설계, 무인 실행 작업의 보안 리뷰를 다룹니다. 「돌아가니까 건드리지 않는」 채로 방치된 평문 비밀번호 정리부터 상담해 주세요.
참고 링크
-
Microsoft Learn, AvoidUsingConvertToSecureStringWithPlainText. PSScriptAnalyzer가 ConvertTo-SecureString의 -AsPlainText 사용을 오류(Severity: Error)로 검출하는 것, 암호화를 우회해 민감 정보를 평문으로 메모리에 노출하는 것, 대체로 Read-Host -AsSecureString이나 SecretStore 모듈이 거론되는 것에 대해. ↩ ↩2 ↩3
-
Microsoft Learn, Add Credential support to PowerShell functions. 자체 함수에 PSCredential 매개변수를 추가하는 방법, 평문 비밀번호가 다양한 로그에 기록되므로 ConvertTo-SecureString -AsPlainText 사용 시 경고되는 이유에 대해. ↩ ↩2 ↩3
-
Microsoft Learn, Get-Credential. Get-Credential이 사용자 이름과 비밀번호 입력을 재촉해 PSCredential 객체를 반환하는 것, Windows PowerShell 5.1에서는 대화 상자·PowerShell 6 이후에는 콘솔에서 입력을 요청하는 것, -Credential 매개변수를 가진 명령에 넘겨 쓰는 것에 대해. ↩ ↩2 ↩3
-
Microsoft Learn, SecureString Class. .NET(Core)의 신규 개발에서 SecureString을 쓰지 않는 것이 권장되는 것, 암호화가 Windows에서만 이루어지고 비 Windows에서는 내부 스토리지가 암호화되지 않는 것, 사용 시에는 평문 표현으로의 변환이 필요한 것, 권장 대체가 프로세스 밖에 저장된 자격 정보에 대한 불투명한 핸들인 것에 대해. ↩ ↩2
-
Microsoft Learn, Advisory Development Guidelines. .NET이 SecureString의 신규 이용을 권장하지 않는 한편, PowerShell은 하위 호환성을 위해 SecureString을 계속 지원하는 것, 평문 문자열보다는 안전하며 콘솔이나 로그에의 우연한 노출을 피하기 위해 쓰이는 것, 쉽게 평문으로 변환할 수 있으므로 주의해서 써야 하는 것에 대해. ↩ ↩2
-
Microsoft Learn, Export-Clixml. Export-Clixml이 자격 정보 객체를 Windows DPAPI로 암호화해 저장하는 것, 복호화할 수 있는 것이 저장한 사용자 계정·저장한 컴퓨터상에서뿐이며 내보내기 파일을 다른 머신·다른 사용자에서 쓸 수 없는 것, 비 Windows(macOS/Linux)에서는 암호화되지 않고 사실상 평문으로 출력되는 것에 대해. ↩ ↩2 ↩3 ↩4 ↩5
-
Microsoft Learn, Overview of the SecretManagement and SecretStore modules. SecretManagement이 확장 볼트에 대한 통일 인터페이스인 것, SecretStore가 로컬 파일에 암호화 저장하는 크로스 플랫폼 확장 볼트인 것, Azure Key Vault·KeePass·자격 정보 관리자(CredMan) 등의 확장 볼트가 존재하는 것, Secret 모듈군이 기능 완성 취급으로 적극 개발을 종료하고 보안 수정만 계속되는 것에 대해. ↩ ↩2 ↩3 ↩4 ↩5 ↩6
-
Microsoft Learn, Use the SecretStore in automation. 무인 실행용으로 SecretStore의 Interaction을 None으로 구성하는 것, 볼트 비밀번호를 Export-Clixml로 DPAPI 암호화한 파일에 저장하고 Unlock-SecretStore로 잠금 해제하는 구성, 이것이 Windows 한정 해법인 것에 대해. ↩ ↩2 ↩3 ↩4
-
Microsoft Learn, Group Managed Service Accounts overview. gMSA가 자동 비밀번호 관리와 SPN 관리의 단순화를 제공하는 관리되는 도메인 계정이며, 비밀번호 관리를 관리자가 아니라 Windows OS가 맡는 것에 대해. ↩ ↩2 ↩3 ↩4
-
Microsoft Learn, Secure group managed service accounts. gMSA 비밀번호가 240바이트 난수 생성인 것, OS가 30일마다 자동 변경하므로 비밀번호 변경 계획이나 서비스 중지가 불필요해지는 것, 온프레미스 서비스의 계정 종류로 gMSA 사용이 권장되는 것에 대해. ↩ ↩2
-
Microsoft Learn, about_Logging_Windows. 스크립트 블록 로그를 켜면 PowerShell이 처리하는 모든 스크립트 블록의 내용이 이벤트 로그에 기록되는 것, 로그 수준을 올리면 스크립트에서 쓰인 자격 정보 등 민감 데이터가 로그에 포함될 수 있는 것, PowerShell 7계에서 스크립트 블록 로그를 켜는 레지스트리 값이
HKLM:\Software\Policies\Microsoft\PowerShellCore\ScriptBlockLogging의EnableScriptBlockLogging인 것, Protected Event Logging이 CMS(암호 메시지 구문)의 공개 키로 이벤트 로그 내용을 암호화하고 비밀 키를 가진 안전한 장소에서 복호화하는 메커니즘인 것에 대해. ↩ ↩2 ↩3 ↩4 ↩5 ↩6 -
Microsoft Learn, ConvertFrom-SecureString. SecureString을 암호화된 표준 문자열로 변환할 수 있는 것, 키를 지정하지 않으면 Windows DPAPI가, Key/SecureKey 지정 시에는 AES가 쓰이는 것, -AsPlainText로 평문 문자열로 직접 변환할 수 있는 것에 대해. ↩ ↩2
-
Microsoft Learn, Import-Clixml. Import-Clixml로 Export-Clixml이 저장한 자격 정보·보안 문자열을 복원할 수 있는 것, 이로써 스크립트 안에 평문 비밀번호를 적는 위험을 피할 수 있는 것에 대해. ↩
-
Microsoft Learn, CryptProtectData function. 보통 암호화한 사용자와 같은 로그온 자격 정보를 가진 사용자만 복호화할 수 있는 것, 암호화와 복호화가 보통 같은 컴퓨터에서 수행되어야 하는 것, 함수가 사용자의 로그온 자격 정보에서 세션 키를 만들어 암호화를 수행하는 것에 대해. ↩
-
Microsoft Learn, Understanding the SecretManagement module. SecretManagement이 등록된 확장 볼트를 통해 비밀을 저장·취득하는 메커니즘인 것, 볼트 등록이 사용자 컨텍스트 단위인 것, $env:LOCALAPPDATA와 DPAPI에의 의존으로 Windows의 관리되는 계정(managed accounts)에서는 현재 동작하지 않는 것에 대해. ↩ ↩2 ↩3
-
Microsoft Learn, Get started with the SecretStore module. Register-SecretVault에서의 SecretStore 등록, Set-Secret/Get-Secret/Get-SecretInfo의 기본 조작, 최초 액세스 시 볼트 비밀번호 설정을 요구받는 것에 대해. ↩
-
Microsoft Learn, Use Azure Key Vault in automation. Az.KeyVault 3.3.0 이후가 SecretManagement 확장을 포함하며, Register-SecretVault로 Azure Key Vault를 SecretManagement 볼트로 등록해 Get-Secret 등으로 조작할 수 있는 것에 대해. ↩ ↩2 ↩3
-
Microsoft Learn, Understanding the security features of SecretManagement and SecretStore. 비밀번호 인증을 완전히 무효화한 경우, 복호화 키가 파일 시스템 권한만으로 보호되며 강한 보안 보호가 필요한 시스템에는 권장되지 않는 것에 대해. ↩
-
Microsoft Learn, What are managed identities for Azure resources?. 관리 ID가 Entra ID상의 ID를 써서 자격 정보를 관리하지 않고 토큰을 받는 메커니즘인 것, 시스템 할당과 사용자 할당의 두 종류와 그 차이에 대해. ↩
-
Microsoft Learn, Manage group Managed Service Accounts. gMSA를 서비스 컨트롤 관리자 구성의 서비스, IIS 애플리케이션 풀, 작업 스케줄러의 작업에서 쓸 수 있는 것, 전제 조건(도메인·포리스트 기능 수준이 Windows Server 2012 이상, Domain Admins/Enterprise Admins 멤버십, KDS 루트 키의 존재와 KdsSvc 운영 로그의 이벤트 ID 4004로 확인, 도메인 컨트롤러가 아닌 곳에서의 작업에 RSAT가 필요한 것), New-ADServiceAccount에서의 작성 절차와 PrincipalsAllowedToRetrieveManagedPassword 지정, Install-ADServiceAccount/Test-ADServiceAccount에 의한 도입과 확인에 대해. ↩ ↩2 ↩3 ↩4
-
Microsoft Learn, Create the Key Distribution Services KDS Root Key. gMSA 비밀번호 생성에 KDS 루트 키가 필요한 것, Add-KdsRootKey -EffectiveImmediately로 작성과 모든 도메인 컨트롤러로의 복제 수렴을 위해 최대 10시간 기다리는 사양, 검증 환경용으로 Add-KdsRootKey -EffectiveTime으로 과거 시각을 지정해 대기 시간을 피하는 절차에 대해. ↩
-
Microsoft Learn, ADMX_EventLogging Policy CSP. EnableProtectedEventLogging 정책이 컴퓨터 구성의 「Windows 구성 요소 > 이벤트 로깅」에 있는 것, 대응하는 레지스트리 키가
Software\Policies\Microsoft\Windows\EventLog\ProtectedEventLogging, 값 이름이EnableProtectedEventLogging인 것에 대해. ↩ -
Microsoft Learn, about_Logging (Windows PowerShell 5.1). Windows PowerShell 5.1에서 스크립트 블록 로그를 켜는 레지스트리 값이
HKLM:\Software\Policies\Microsoft\Windows\PowerShell\ScriptBlockLogging의EnableScriptBlockLogging인 것에 대해. ↩
관련 기사
같은 태그를 공유하는 최신 기사입니다. 더 가까운 주제로 지식을 넓힐 수 있습니다.
PowerShell Remoting(WinRM) 입문 ── 여러 대의 Windows를 일괄 관리하기
PowerShell Remoting(WinRM)으로 여러 대의 Windows를 일괄 관리하는 입문. 동작 원리와 포트 5985/5986, Enable-PSRemoting으로 일어나는 일, 워크그룹의 TrustedHosts, Invoke-Comma...
PowerShell의 실행 정책과 스크립트 서명 ── 'Bypass로 덮어 버리는' 운영에서 벗어나는 실무 가이드
PowerShell의 실행 정책은 '보안 경계가 아니라 안전장치'입니다. RemoteSigned 등의 차이, 범위의 우선순위, Mark of the Web과 Unblock-File, 스크립트 서명, 사내 배포의 현실적인 운영까지 정리합니다.
PowerShell 스크립트가 느릴 때 볼 곳 ── 배열·파이프라인·매칭의 핵심
PowerShell 스크립트가 느려지는 대표적인 원인을 정리합니다. 배열의 +=가 O(n^2)가 되는 이유, 파이프라인과 foreach의 차이, 매칭의 해시 테이블화, 파일 I/O 개선, 올바른 측정 방법까지 실무 관점에서 설명합니다.
Write-Host를 그만두기 ── PowerShell의 출력 스트림과 로그 설계
PowerShell 6가지 출력 스트림의 구분 사용, Write-Host가 가진 문제와 올바른 쓰임새, 함수 반환값이 오염되는 원인, -Verbose와 -InformationVariable로 호출 측에서 제어하는 방법, 구조화 로그를 남기는 방법...
PowerShell의 병렬 처리 ── ForEach-Object -Parallel과 Job의 구분
ForEach-Object -Parallel·Start-ThreadJob·Start-Job의 차이와 구분, $using:와 스레드 안전성, ThrottleLimit 정하는 법, 오히려 느려지는 경우까지 실무 관점에서 정리합니다.
관련 토픽
이 기사와 가까운 토픽 페이지입니다. 기사를 출발점 삼아 관련 서비스와 다른 기사로 이어집니다.
Windows 기술 토픽
Windows 개발, 장애 조사, 기존 자산 활용에 관한 KomuraSoft LLC 기사를 모은 토픽 허브입니다.
이 주제와 연결되는 서비스
이 기사는 다음 서비스 페이지로 이어집니다. 가까운 입구부터 확인해 주세요.
Windows 앱 개발
상주 처리, 장비 연동, 운영 로그, 유지 보수 가능한 구조가 필요한 Windows 데스크톱 애플리케이션을 지원합니다.
자주 묻는 질문
이 기사 주제에 대해 상담 시 자주 나오는 질문을 모았습니다.
- PowerShell 스크립트에 비밀번호를 평문으로 적으면 안 되는 이유는 무엇인가요?
- 스크립트는 복사되고, 공유되며, 이력에 남는 것을 전제로 한 산출물이기 때문입니다. Git에 한 번 커밋하면 이력에서 지우기 어렵고, 공유 폴더에 두면 읽기 권한이 있는 전원에게 보이며, 스크립트 블록 로그나 트랜스크립트에는 명령 내용이 그대로 기록됩니다. 즉 평문 비밀번호는 「파일을 본 시점에 유출이 확정」되는 구조를 만듭니다. 먼저 유출 경로를 막는 것이 아니라, 비밀번호를 스크립트 밖의 보호된 장소로 빼내는 것이 대책의 본류입니다.
- Export-Clixml로 저장한 자격 정보는 어느 정도까지 안전한가요?
- Windows에서는 DPAPI(데이터 보호 API)로 암호화되며, 저장한 사용자 계정이면서 같은 컴퓨터에서만 복호화할 수 있습니다. 파일이 도난당해도 다른 머신·다른 사용자에서는 열 수 없으므로, 무인 실행용 저장 위치로는 실용적인 선택입니다. 다만 동일 계정으로 동작하는 프로세스에서는 복호화할 수 있어 만능은 아니며, macOS나 Linux에서는 암호화되지 않고 사실상 평문으로 출력된다는 점에 주의가 필요합니다. 작업 스케줄러에서 쓸 때는 작업의 실행 계정 자신이 저장 파일을 만들어야 합니다.
- SecureString은 지금도 써야 하나요?
- .NET 공식 문서는 신규 개발에서 SecureString 사용을 권장하지 않는다고 명시합니다. 암호화는 Windows에서만 이루어지며, 비 Windows에서는 내부가 암호화되지 않습니다. 또한 쓰는 순간에는 결국 평문으로 되돌려야 합니다. 한편 PowerShell 세계에서는 Get-Credential이나 SecretManagement 같은 표준 도구가 SecureString을 전제로 하며, 평문 문자열로 들고 다니는 것보다는 노출이 줄어들므로, 당분간은 「PowerShell의 표준적인 전달 형식」으로 다루는 것이 현실적입니다. 직접 암호화 메커니즘을 만드는 재료로는 쓰지 마세요.
- 작업 스케줄러의 무인 실행에서 SecretStore를 쓰면 비밀번호 입력에서 멈추지 않나요?
- 기본 구성 그대로면 멈춥니다. SecretStore는 기본적으로 볼트 비밀번호를 요구하고 대화형 프롬프트를 내기 때문입니다. 무인 실행에서는 Interaction을 None으로 하고, 볼트 비밀번호를 DPAPI로 보호한 파일(Export-Clixml)에서 읽어 Unlock-SecretStore로 잠금 해제하는 구성을 공식 문서가 안내합니다. 다만 이는 「볼트의 키를 DPAPI로 지키는」 구성이며, 결국 DPAPI의 제약(동일 사용자·동일 머신)을 이어받습니다. 그 앞에서 gMSA나 실행 계정에 권한을 부여해 자격 정보 자체를 없앨 수 없는지 먼저 검토하세요.
- 애초에 자격 정보를 저장하지 않는 방법은 있나요?
- 있습니다. 오히려 그것이 제1선택입니다. Windows의 작업이나 서비스는 실행 계정의 권한으로 동작하므로, 연결 대상(공유 폴더, SQL Server의 Windows 인증 등)에 실행 계정 자신의 권한을 부여하면 스크립트는 비밀번호를 전혀 갖지 않아도 됩니다. 실행 계정을 gMSA(그룹 관리 서비스 계정)로 하면, 비밀번호는 240바이트 난수 값을 OS가 30일마다 자동 갱신하고, 사람은 아무도 비밀번호를 모르는 상태를 만들 수 있습니다. gMSA는 작업 스케줄러의 작업에도 쓸 수 있습니다.