Администраторы часто при написании сценариев автоматизации на PowerShell сохраняют пароли непосредственно в теле PoSh скрипта. Как вы понимаете, это крайне небезопасно при использовании в продуктивной среде, т.к. пароль в открытом виде могут увидеть другие пользователи сервера или администраторы. Поэтому желательно использовать более безопасный метод использования паролей в скриптах PowerShell, или шифровать пароли, если нельзя пользоваться интерактивным вводом.
Безопасно можно запросить от пользователя ввести пароль в скрипте интерактивно с помощью командлета Get-Credential. Например, запросим имя и пароль пользователя и сохраним его в объекте типа PSCredential:
$Cred = Get-Credential
При обращении к свойствам переменной можно узнать имя ползователя, который был указан.
$Cred.Username
Но при попытке вывести пароль, вернется текст System.Security.SecureString, т.к. пароль теперь хранится в виде SecureString.
$Cred.Password
Объект PSCredential, который мы сохранили в переменной $Cred теперь можно использоваться в командлетах, которые поддерживают данный вид объектов.
Параметры $Cred.Username и $Cred.Password можно использовать в командлетах, которые не поддерживают объекты PSCredential, но требуют отдельного ввода имени и пароля пользователя.
Также для запроса пароля пользователя можно использовать команлет Read-Host с атрибутом AsSecureString:
$pass = Read-Host "Введите пароль" –AsSecureString
В данном случае, вы также не сможете увидеть содержимое переменной $pass, в которой хранится пароль.
В рассмотренных выше способах использования пароля в скриптах PowerShell предполагался интерактивный ввод пароля при выполнении скрипта. Но этот способ не подойдет для различных сценариев, запускаемых автоматически или через планировщик.
В этом случае удобнее зашифровать данные учетной записи (имя и пароль) и сохранить их в зашифрованном виде в текстовый файл на диске или использовать непосредственно в скрипте.
Итак, с помощью комадлета ConvertFrom-SecureString можно преобразовать пароль из формата SecureString в шифрованную строку (шифрование выполняется с помощью Windows Data Protection API — DPAPI). Вы можете вывести шифрованный пароль на экран или сохранить в файл:
$Cred.Password| ConvertFrom-SecureString | Set-Content c:\ps\passfile.txt
Чтобы использовать зашифрованный пароль из файла нужно выполнить обратное преобразование в формат Securestring с помощью командлета ConvertTo-SecureString:
$username = ″corp\administrator″
$pass = Get-Content c:\ps\passfile.txt | ConvertTo-SecureString
$creds = New-Object -TypeName System.Management.Automation.PSCredential -ArgumentList $username, $pass
Таким образом в переменной $creds мы получили объект PSCredential с учетными данными пользователя.
Однако, если попробовать скопировать файл passfile.txt на другой компьютер или использовать его под другим пользователем (не тем, под которым создавался пароль), вы увидите, что переменная $creds.password пустая и не содержит пароля. Дело в том, что шифрованием с помощью DPAPI выполняется с помощью ключей, хранящихся в профиле пользователя. Без этих ключей на другом компьютере вы не сможете расшифровать файл с паролем.
ConvertTo-SecureString : Ключ не может быть использован в указанном состоянии.
"Не удается обработать аргумент, так как значением аргумента "password" является NULL.
Укажите для аргумента "password" значение, отличное от NULL."
Таким образом, если скрипт будет запускаться под другим (сервисным) аккаунтом или на другом компьютере, необходимо использовать другой механизм шифрования, отдичный от DPAPI. Внешний ключ шифрования можно указать с помощью параметров –Key или –SecureKey.
Например, вы можете с помощью PowerShell сгенерировать 256 битный AES ключ, который можно использовать для расшифровки файла. Сохраним ключ в текстовый файл password_aes.key.
$AESKey = New-Object Byte[] 32
[Security.Cryptography.RNGCryptoServiceProvider]::Create().GetBytes($AESKey)
$AESKey | out-file C:\ps\password_aes.key
Теперь можно сохранить пароль в файл с помощью данного ключа:
$Cred.Password| ConvertFrom-SecureString -Key (get-content C:\ps\password_aes.key)| Set-Content c:\ps\passfile.txt
Таким образом у нас получилось два файла: файл с зашифрованным паролем (passfile.txt) и файл с ключом шифрования (password_aes.key).
Их можно перенести на другой компьютер и попытаться из PowerShell получить пароль из файла (можно разместить файл ключа в сетевом каталоге)
$pass = Get-Content c:\ps\passfile.txt | ConvertTo-SecureString -Key (get-content \\Server1\Share\password_aes.key)
$pass
Если не хочется заморачивать с отдельным файлом с AES ключом, можно зашить ключ шифрования прямо в скрипт. В этом случае вместо ключа в обоих случая нужно использовать
[Byte[]] $key = (1..16)
$Cred.Password| ConvertFrom-SecureString –Key $key| Set-Content c:\ps\passfile.txt
А для расшифровки:
[Byte[]] $key = (1..16)
$pass = Get-Content c:\ps\passfile.txt | ConvertTo-SecureString -Key $key
Как вы видите пароль не пустой, значит он был успешно расшифрован и может быть использован на других компьютерах.
И напоследок, самый печальный момент. Пароль из объекта PSCredential в открытом виде вытаскивается очень просто:
$Cred.GetNetworkCredential().password
Можно извлечь пароль в текстовом виде и из SecureString:
$BSTR = [System.Runtime.InteropServices.Marshal]::SecureStringToBSTR($pass)
[System.Runtime.InteropServices.Marshal]::PtrToStringAuto($BSTR)
Как вы понимаете, именно поэтому нежелательно сохранять пароли привилегированных учетных записей, таких как Domain Admins где бы то ни было кроме DC.