PowerShell about_Signing — 스크립트 서명하기

PowerShell about_Signing — 스크립트 서명하기

PowerShell에는 실행 정책(Execution Policy)이라는 게 있어요. 컴퓨터에서 아무 스크립트나 맘대로 실행되면 위험하니까, 정책으로 걸러내는 거죠. 그런데 AllSigned(모두 서명)나 RemoteSigned(원격 서명) 정책을 쓰면 디지털 서명이 없는 스크립트는 실행이 막혀요. 이 문서에서는 스크립트에 서명을 다는 방법, 그리고 서명 없이도 실행할 수 있는 경우를 하나씩 정리해 드릴게요.

출처: Microsoft Learn — about_Signing

본문

이 문서가 다루는 범위

이 내용은 Windows에서 실행되는 PowerShell에만 해당해요. 우선 PowerShell이 어떤 파일의 Authenticode 서명을 확인하는지부터 볼게요.

  • .ps1 스크립트 파일
  • .psm1 모듈 파일
  • .psd1 모듈 매니페스트·데이터 파일
  • .ps1xml 형식(type)·서식(format) XML 파일
  • .cdxml CDXML 스크립트 파일
  • .xaml XAML 스크립트 파일

실행 정책 자체에 대한 설명은 about_Execution_Policies 문서를 함께 보시면 좋아요.

서명된 스크립트의 실행을 허용하려면

처음 PowerShell을 시작하면 기본값인 Restricted(제한) 정책이 적용되는 경우가 많아요. Restricted 정책 아래에서는 모든 스크립트가 실행되지 않아요. 지금 내 컴퓨터에 적용된 정책이 뭔지 확인하고 싶다면 이렇게 입력하면 돼요.

Get-ExecutionPolicy

RemoteSigned 정책은 서명된 스크립트내가 로컬에서 직접 만든 서명 없는 스크립트는 실행을 허용해 줘요. 이 정책을 적용하려면 PowerShell을 관리자 권한으로 실행(Run as Administrator)한 뒤, 아래 명령으로 정책을 바꾸면 돼요.

Set-ExecutionPolicy RemoteSigned

자세한 내용은 Set-ExecutionPolicy cmdlet 도움말을 참고하세요. 서명된 스크립트가 실행되려면 신뢰할 수 있는 게시자의 디지털 서명이 있어야 하고, 코드 서명 인증서는 이 컴퓨터에서 신뢰하는 인증 기관(certification authority)이 발급한 것이어야 해요. 자체 서명 인증서는 컴퓨터의 신뢰할 수 있는 루트 인증 기관(Trusted Root Certificates) 저장소에 설치되어 있어야 해요.

RemoteSigned 정책에서 서명 없는 스크립트 실행하기

실행 정책이 RemoteSigned라면, 인터넷에서 내려받은 스크립트는 이메일이나 메신저로 받은 것까지 포함해서 실행되지 않아요. 내려받은 스크립트를 실행하려 하면 PowerShell이 이런 오류 메시지를 보여줘요.

The file <file-name> cannot be loaded. The file <file-name> is not
digitally signed. The script will not execute on the system. Please see
"Get-Help about_Signing" for more details.

스크립트는 실행 프로그램과 똑같은 효과를 가지니까, 실행하기 전에 코드를 꼭 검토해서 신뢰할 수 있는지 확인하셔야 해요. 서명 없는 스크립트를 실행하는 방법은 두 가지예요. Unblock-File cmdlet을 쓰거나, 아래 절차를 따라 하면 돼요.

  1. 스크립트 파일을 컴퓨터에 저장한다.
  2. 시작(Start)내 컴퓨터(My Computer) 를 클릭하고 저장한 스크립트 파일을 찾는다.
  3. 스크립트 파일을 오른쪽 클릭하고 속성(Properties) 을 클릭한다.
  4. 차단 해제(Unblock) 를 클릭한다.

만약 인터넷에서 내려받은 스크립트에 디지털 서명은 있는데, 그 게시자를 아직 신뢰하기로 선택하지 않았다면 PowerShell이 이런 메시지를 보여줘요.

Do you want to run software from this untrusted publisher?
The file <file-name> is published by CN=<publisher-name>. This
publisher is not trusted on your system. Only run scripts
from trusted publishers.

[V] Never run  [D] Do not run  [R] Run once  [A] Always run
[?] Help (default is "D"):

게시자를 신뢰한다면 Run once(한 번 실행) 이나 Always run(항상 실행) 을 선택하면 되고, 신뢰하지 않는다면 Never run(실행 안 함) 이나 Do not run(실행하지 않음) 을 선택하면 돼요. Never run이나 Always run을 선택하면 다음부터는 이 게시자에 대해 더 이상 묻지 않아요.

스크립트 서명 방법

내가 직접 쓴 스크립트든 다른 곳에서 받아온 스크립트든 서명을 달 수 있어요. 서명하기 전에 각 명령을 하나씩 검토해서 안전하게 실행 가능한지 확인하는 걸 잊지 마세요. 스크립트 파일 서명 방법은 Set-AuthenticodeSignature 문서를 참고해요.

New-SelfSignedCertificate cmdlet은 PowerShell 3.0의 PKI 모듈에 도입됐는데, 테스트용으로 알맞은 자체 서명 인증서를 만들어 줘요. 스크립트에 디지털 서명을 추가하려면 코드 서명 인증서로 서명해야 하는데, 스크립트 파일 서명에 알맞은 인증서는 두 종류예요.

  • 인증 기관이 만든 인증서: 유료로 공인 인증 기관이 신원을 확인해 주고 코드 서명 인증서를 발급해 줘요. 신뢰할 수 있는 인증 기관에서 인증서를 구입하면, 그 기관을 신뢰하는 다른 Windows 컴퓨터의 사용자들과도 스크립트를 공유할 수 있어요.
  • 내가 직접 만든 인증서: 내 컴퓨터가 곧 인증 기관 역할을 하는 자체 서명 인증서를 만들 수 있어요. 공짜로 만들 수 있고, 내 컴퓨터에서 스크립트를 쓰고 서명하고 실행하는 데 손색없어요. 다만 자체 서명 인증서로 서명한 스크립트는 다른 컴퓨터에서는 실행되지 않아요.

자체 서명 인증서는 테스트 목적으로만 써야 하고, 공유할 스크립트에는 적합하지 않아요. 또 자체 서명 인증서를 만들 때는 프라이빗 키 강력한 보호(strong private key protection)를 꼭 켜두세요. 그래야 악성 프로그램이 네 이름으로 스크립트에 서명하는 걸 막을 수 있어요. 설정 방법은 이 문서 맨 뒤에 나와요.

자체 서명 인증서 만들기

자체 서명 인증서를 만들려면 PKI 모듈의 New-SelfSignedCertificate cmdlet을 쓰면 돼요. PowerShell 3.0에 도입된 모듈이에요.

$params = @{
    Subject = 'CN=PowerShell Code Signing Cert'
    Type = 'CodeSigning'
    CertStoreLocation = 'Cert:\CurrentUser\My'
    HashAlgorithm = 'sha256'
}
$cert = New-SelfSignedCertificate @params

Makecert.exe 사용하기

예전 버전의 Windows에서는 인증서 생성 도구 MakeCert.exe로 자체 서명 인증서를 만들었어요. 이 도구는 Microsoft .NET SDK(버전 1.1 이상)와 Microsoft Windows SDK에 포함되어 있어요. 문법과 매개 변수 설명은 Certificate Creation Tool (MakeCert.exe) 문서를 참고하시고, SDK 명령 프롬프트 창에서 아래 명령을 실행하면 돼요.

참고 첫 번째 명령은 컴퓨터의 로컬 인증 기관을 만들고, 두 번째 명령은 그 인증 기관에서 개인 인증서를 생성해요. 명령은 그대로 복사하거나 입력하면 되고, 인증서 이름을 바꾸는 것 외에는 다른 수정이 필요 없어요.

makecert -n "CN=PowerShell Local Certificate Root" -a sha256 `
-eku 1.3.6.1.5.5.7.3.3 -r -sv root.pvk root.cer `
-ss Root -sr localMachine

makecert -pe -n "CN=PowerShell User" -ss MY -a sha256 `
-eku 1.3.6.1.5.5.7.3.3 -iv root.pvk -ic root.cer

MakeCert.exe 도구가 프라이빗 키 암호를 물어볼 거예요. 이 암호가 있어야만 인증서를 쓰거나 접근할 수 있으니까, 아무나 못 쓰게 해 주는 거예요. 나중에 인증서를 꺼내 쓸 때 필요하므로 반드시 기억할 수 있는 암호를 만들어 입력하세요.

인증서가 제대로 생성됐는지 확인하려면 아래 명령으로 컴퓨터의 인증서 저장소에서 인증서를 가져와 보면 돼요. 파일 시스템 디렉터리에는 인증서 파일이 생기지 않으니 헷갈리지 마세요. PowerShell 프롬프트에서 이렇게 입력해요.

Get-ChildItem Cert:\CurrentUser\my -CodeSigning

이 명령은 PowerShell 인증서 공급자(Certificate provider)를 이용해 인증서 정보를 보여줘요. 인증서가 만들어졌다면 아래처럼 인증서를 식별하는 지문(thumbprint) 이 출력돼요.

Directory: Microsoft.PowerShell.Security\Certificate::CurrentUser\My

Thumbprint                                Subject
----------                                -------
4D4917CB140714BA5B81B96E0B18AAF2C4564FDF  CN=PowerShell User

스크립트 서명하기

자체 서명 인증서를 만들었다면 이제 스크립트에 서명할 수 있어요. AllSigned 실행 정책을 쓴다면, 서명한 스크립트는 내 컴퓨터에서 실행할 수 있어요. 아래는 스크립트에 서명해 주는 예제 스크립트 Add-Signature.ps1이에요. 다만 AllSigned 정책을 쓰고 있다면, 이 Add-Signature.ps1 자체도 실행 전에 먼저 서명해 두어야 해요.

중요 PowerShell 7.2 이전 버전에서는 스크립트를 ASCII 또는 UTF8NoBOM 인코딩으로 저장해야 했어요. PowerShell 7.2 이상부터는 어떤 인코딩 형식이든 서명된 스크립트를 지원해요.

이 스크립트를 쓰려면 아래 텍스트를 텍스트 파일에 복사한 뒤 Add-Signature.ps1이라는 이름으로 저장하세요.

## SIGNS A FILE
[CmdletBinding()]
param(
    [Parameter(Mandatory=$true)]
    [string] $File
)

$cert = Get-ChildItem Cert:\CurrentUser\My -CodeSigningCert |
    Select-Object -First 1

Set-AuthenticodeSignature -FilePath $File -Certificate $cert

Add-Signature.ps1로 스크립트에 서명하려면 PowerShell 프롬프트에서 아래 명령을 입력하면 돼요.

$cert = Get-ChildItem Cert:\CurrentUser\My -CodeSigningCert |
    Select-Object -First 1

Set-AuthenticodeSignature Add-Signature.ps1 $cert

Set-AuthenticodeSignature cmdlet은 파일 끝에 주석 블록 형태로 서명을 추가해요. 그 주석 블록은 # SIG #로 시작하고 끝나요.

서명을 마치면 스크립트는 로컬 컴퓨터에서 실행할 수 있어요. 그런데 신뢰할 수 있는 인증 기관의 디지털 서명을 요구하는 실행 정책을 쓰는 컴퓨터에서는 실행되지 않아요. 실행하려 하면 PowerShell이 이런 오류 메시지를 보여줘요.

The file C:\remote_file.ps1 cannot be loaded. The signature of the
certificate cannot be verified.
At line:1 char:15
+ .\ remote_file.ps1 <<<<

PowerShell이 이런 메시지를 보여주면서 실행할 때, 내가 직접 쓰지 않은 스크립트라면 서명 없는 스크립트를 대하듯 하면 돼요. 코드를 검토해 신뢰할 수 있는지 판단하세요.

프라이빗 키 강력한 보호 사용하기

내 컴퓨터에 프라이빗 키와 인증서가 있다면, 악성 프로그램이 내 대신 스크립트에 서명해서 PowerShell이 그걸 실행하도록 만들 수도 있어요. 이런 자동 서명을 막으려면 인증서 관리자 Certmgr.exe로 서명 키와 인증서를 .pfx 파일로 내보내 두면 돼요. 인증서 관리자는 Microsoft .NET SDK, Microsoft Windows SDK, Internet Explorer에 포함되어 있어요.

인증서를 내보내려면:

  1. 인증서 관리자(Certificate Manager)를 시작한다.
  2. PowerShell Local Certificate Root가 발급한 인증서를 선택한다.
  3. 내보내기(Export) 를 클릭해 인증서 내보내기 마법사를 시작한다.
  4. 예, 프라이빗 키를 내보냅니다(Yes, export the private key) 를 선택하고 다음(Next) 을 클릭한다.
  5. 강력한 보호 사용(Enable strong protection) 을 선택한다.
  6. 암호를 입력하고 한 번 더 입력해 확인한다.
  7. .pfx 확장자를 가진 파일 이름을 입력한다.
  8. 마침(Finish) 을 클릭한다.

인증서를 다시 가져오려면:

  1. 인증서 관리자(Certificate Manager)를 시작한다.
  2. 가져오기(Import) 를 클릭해 인증서 가져오기 마법사를 시작한다.
  3. 내보내기 과정에서 만든 .pfx 파일의 위치로 이동한다.
  4. 암호 페이지에서 프라이빗 키 강력한 보호 사용(Enable strong private key protection) 을 선택하고, 내보내기 때 지정한 암호를 입력한다.
  5. 개인(Personal) 인증서 저장소를 선택한다.
  6. 마침(Finish) 을 클릭한다.

서명이 만료되지 않게 하기

스크립트의 디지털 서명은 서명 인증서가 만료될 때까지, 또는 타임스탬프 서버(timestamp server)가 서명 인증서가 유효했던 시점에 스크립트가 서명됐음을 검증해 주는 한 유효해요. 대부분의 서명 인증서는 유효 기간이 1년뿐이기 때문에, 타임스탬프 서버를 쓰면 사용자가 앞으로도 여러 해 동안 스크립트를 쓸 수 있게 돼요.

더 알아보기