Защита и шифрование паролей в скриптах PowerShell

Защита и шифрование паролей в скриптах PowerShell

Администраторы часто при написании сценариев автоматизации на PowerShell сохраняют пароли непосредственно в теле PoSh скрипта. Как вы понимаете, это крайне небезопасно при использовании в продуктивной среде, т.к. пароль в открытом виде могут увидеть другие пользователи сервера или администраторы. Поэтому желательно использовать более безопасный метод использования паролей в скриптах PowerShell, или шифровать пароли, если нельзя пользоваться интерактивным вводом.

Безопасно можно запросить от пользователя ввести пароль в скрипте интерактивно с помощью командлета Get-Credential. Например, запросим имя и пароль пользователя и сохраним его в объекте типа PSCredential:

$Cred = Get-Credential

powershell Get-Credential

При обращении к свойствам переменной можно узнать имя ползователя, который был указан.

$Cred.Username

Но при попытке вывести пароль, вернется текст System.Security.SecureString, т.к. пароль теперь хранится в виде SecureString.

$Cred.Password

пароль хранится в System.Security.SecureString

Объект PSCredential, который мы сохранили в переменной $Cred теперь можно использоваться в командлетах, которые поддерживают данный вид объектов.

Параметры $Cred.Username и $Cred.Password можно использовать в командлетах, которые не поддерживают объекты PSCredential, но требуют отдельного ввода имени и пароля пользователя.

Также для запроса пароля пользователя можно использовать команлет Read-Host с атрибутом AsSecureString:
$pass = Read-Host "Введите пароль" –AsSecureString

Read-Host AsSecureString

В данном случае, вы также не сможете увидеть содержимое переменной $pass, в которой хранится пароль.

В рассмотренных выше способах использования пароля в скриптах PowerShell предполагался интерактивный ввод пароля при выполнении скрипта. Но этот способ не подойдет для различных сценариев, запускаемых автоматически или через планировщик.

В этом случае удобнее зашифровать данные учетной записи (имя и пароль) и сохранить их в зашифрованном виде в текстовый файл на диске или использовать непосредственно в скрипте.

Итак, с помощью комадлета ConvertFrom-SecureString можно преобразовать пароль из формата SecureString в шифрованную строку (шифрование выполняется с помощью Windows Data Protection API — DPAPI). Вы можете вывести шифрованный пароль на экран или сохранить в файл:

$Cred.Password| ConvertFrom-SecureString | Set-Content c:pspassfile.txt

ConvertFrom-SecureString

Чтобы использовать зашифрованный пароль из файла нужно выполнить обратное преобразование в формат Securestring с помощью командлета ConvertTo-SecureString:

$username = ″corpadministrator″
$pass = Get-Content c:pspassfile.txt | ConvertTo-SecureString
$creds = New-Object -TypeName System.Management.Automation.PSCredential -ArgumentList $username, $pass

получение пароля из файла с помощью ConvertTo-SecureString

Таким образом в переменной $creds мы получили объект PSCredential с учетными данными пользователя.

Однако, если попробовать скопировать файл passfile.txt на другой компьютер или использовать его под другим пользователем (не тем, под которым создавался пароль), вы увидите, что переменная $creds.password пустая и не содержит пароля. Дело в том, что шифрованием с помощью DPAPI выполняется с помощью ключей, хранящихся в профиле пользователя. Без этих ключей на другом компьютере вы не сможете расшифровать файл с паролем.
ConvertTo-SecureString : Ключ не может быть использован в указанном состоянии.
"Не удается обработать аргумент, так как значением аргумента "password" является NULL.
Укажите для аргумента "password" значение, отличное от NULL."

null-password

Таким образом, если скрипт будет запускаться под другим (сервисным) аккаунтом или на другом компьютере, необходимо использовать другой механизм шифрования, отдичный от DPAPI. Внешний ключ шифрования можно указать с помощью параметров –Key или –SecureKey.

Например, вы можете с помощью PowerShell сгенерировать 256 битный AES ключ, который можно использовать для расшифровки файла. Сохраним ключ в текстовый файл password_aes.key.

$AESKey = New-Object Byte[] 32
[Security.Cryptography.RNGCryptoServiceProvider]::Create().GetBytes($AESKey)
$AESKey | out-file C:pspassword_aes.key

генерация aes ключа для шифрования пароля

Теперь можно сохранить пароль в файл с помощью данного ключа:

$Cred.Password| ConvertFrom-SecureString -Key (get-content C:pspassword_aes.key)| Set-Content c:pspassfile.txt

зашифрованный файл с паролем powershell

Не забывайте, что если в Powershell скрипте у вас указывается доменная учетная запись, и на нее действует политика регулярной смены пароля, вам придется обновлять данный файл при каждой смене пароля (вы можете создать для определенных учёток отдельную политику паролей с помощью множественных политик паролей FGPP.

Таким образом у нас получилось два файла: файл с зашифрованным паролем (passfile.txt) и файл с ключом шифрования (password_aes.key).

Их можно перенести на другой компьютер и попытаться из PowerShell получить пароль из файла (можно разместить файл ключа в сетевом каталоге)

$pass = Get-Content c:pspassfile.txt | ConvertTo-SecureString -Key (get-content Server1Sharepassword_aes.key)
$pass

ConvertTo-SecureString -key

Если не хочется заморачивать с отдельным файлом с AES ключом, можно зашить ключ шифрования прямо в скрипт. В этом случае вместо ключа в обоих случая нужно использовать

[Byte[]] $key = (1..16)
$Cred.Password| ConvertFrom-SecureString –Key $key| Set-Content c:pspassfile.txt

А для расшифровки:

[Byte[]] $key = (1..16)
$pass = Get-Content c:pspassfile.txt | ConvertTo-SecureString -Key $key

простой ключ aes

Как вы видите пароль не пустой, значит он был успешно расшифрован и может быть использован на других компьютерах.

Совет. Необходимо ограничить доступ к файлу с AES ключом, чтобы только пользователь или аккаунт, под которым запускается скрипт имел к нему доступ. Внимательно проверьте NTFS разрешения на файл password_aes.key при размещении его в сетевом каталоге.

И напоследок, самый печальный момент. Пароль из объекта PSCredential в открытом виде вытаскивается очень просто:

$Cred.GetNetworkCredential().password

$Cred.GetNetworkCredential().password

Можно извлечь пароль в текстовом виде и из SecureString:

$BSTR = [System.Runtime.InteropServices.Marshal]::SecureStringToBSTR($pass)
[System.Runtime.InteropServices.Marshal]::PtrToStringAuto($BSTR)

SecureStringToBSTR получение пароля в открытом виде

Как вы понимаете, именно поэтому нежелательно сохранять пароли привилегированных учетных записей, таких как Domain Admins где бы то ни было кроме DC.

Qiziqarli malumotlar
Защита и шифрование паролей в скриптах PowerShell