Сохранение учетных данных довольно часто используется в PowerShell скриптах для автоматизации заданий, либо при делегировании полномочий для выполнения каких-либо административных задач.
По умолчанию скрипты выполняется от имени пользователя\процесса, его запустившего. Однако многие командлеты имеют параметр Credential, позволяющий указывать альтернативные учетные данные. Для примера возьмем командлет Get-WmiObject и попробуем посмотреть тип процессора на удаленном компьютере SRV1 с помощью команды:
Get-WmiObject -Class win32_processor -ComputerName SRV1
Как и ожидалось, команда завершилась с ошибкой (Access is denied), ведь у текущего пользователя нет прав на удаленном компьютере. Ситуация вполне стандартная, для ее разрешения достаточно указать альтернативные учетные данные. Cохраним учетные данные в переменной $credentials, а затем добавим их в командлет Get-WmiObject с помощью параметра Credential:
$credentials = Get-Credential contoso\administrator
Get-WmiObject -Class win32_processor -ComputerName SRV1 -Credential $credentials
Как видите, теперь удаленная команда выполнилась успешно.
Чтобы лучше понять, что из себя представляют сохраненные учетные данные в PowerShell, посмотрим свойства переменной $credentials. Переменная имеет тип данных Automation.PSCredential, а сами учетные данные хранятся в ее свойствах UserName и Password. Имя пользователя хранится в открытом виде (string), а пароль — в формате защищенной строки (securestring).
Поскольку вводить каждый раз пароль не всегда возможно, то нам надо как-то сохранить его. Например, вывести в зашифрованном виде в обычный текстовый файл:
$credentials.Password | ConvertFrom-SecureString | Set-Content pass.txt
Теперь для того чтобы использовать сохраненный пароль, надо вытащить содержимое файла и преобразовать обратно в формат Securestring. Выглядеть это будет примерно так:
$user = ″contoso\administrator″
$password = Get-Content pass.txt | ConvertTo-SecureString
$credentials = New-Object -TypeName System.Management.Automation.PSCredential -ArgumentList $user, $password
Полученные таким образом Credentials можно использовать для выполнения удаленной команды.
Вроде бы все получилось, учетные данные сохранены. Однако не все так просто. Если мы соберем все команды в скрипт, скопируем его (вместе с файлом, содержащим пароль) на другой компьютер и там попробуем выполнить, то получим ошибку. Если присмотреться повнимательней, то видно, что ошибка произошла из за отсутствия пароля (argument ″password″ is null).
Дело в том, что при сохранении в файл пароль шифруется с помощью механизма Windows Data Protection API (DPAPI). Это встроенный в систему программный интерфейс, использующийся для шифрования и обеспечения безопасности Internet Explorer, Outlook, IIS и многих других компонентов Windows.
Шифрование пароля производится с помощью ключей, создаваемых операционной системой. Ключи эти создаются из пользовательских учетных данных и хранятся локально, в профиле пользователя. Без этих ключей зашифрованные данные невозможно использовать. Проще говоря, сохраненные таким образом учетные данные нельзя перенести на другой компьютер или файловую шару, их можно использовать только локально, на том компьютере, на котором они были получены.
Чтобы отвязать зашифрованные данные от локальной машины ключ, которым будут зашифрованы данные, можно задать вручную. Делается это с помощью параметра key, допустимая длина ключа 16, 24 или 32 бит. Например:
$credentials = Get-Credential contoso\administrator
$key = @(1..24)
$credentials.Password | ConvertFrom-SecureString -Key $key | Set-Content pass.txt
Ключ необходимо запомнить\сохранить и при обратном преобразовании указать его. Получится как то так:
$user = ″contoso\administrator″
$password = Get-Content pass.txt | ConverTo-SecureString -Key $key
$credentials = New-Object -TypeName System.Management.Automation.PSCredential -ArgumentList $user, $password
Теперь мы можем спокойно переносить скрипт и без помех использовать его на других компьютерах.
И в завершение, чтобы у вас не было особых иллюзий по поводу безопасности — зашифрованные пароли легко выдергиваются из сохраненных учетных данных, причем открытым текстом. Например, из PSCredential пароль вытаскивается очень просто:
$credentials.GetNetworkCredential().Password
И чуть сложнее из SecureString:
$BSTR = [System.Runtime.InteropServices.Marchall]::SecureStringToBSTR($credentials.Password)
[System.Runtime.InteropServices.Marchall]::PtrToStringAuto($BSTR)
Как видите, сохранение учетных данных небезопасно, поэтому старайтесь использовать учетные записи с минимальным необходимым набором прав. Не стоит подобным образом хранить пароли пользователей, входящих в группу Domain\Enterprise Admins (и им подобных).
ConverTo надо заменить на ConvertTo