Заметки о Windows и других программных продуктах Microsoft...

Сохранение учетных данных в PowerShell

Сохранение учетных данных в PowerShell

Сохранение учетных данных довольно часто используется в 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

Поправил.

Александр

Не везде поправил. Тут осталось:
$password = Get-Content pass.txt | ConverTo-SecureString -Key $key

спасибо за статью.
описка, если заменить
[System.Runtime.InteropServices.Marchall] на
[System.Runtime.InteropServices.Marcshal] — так работает

А можно сделать как-то прозрачную аутенификацию? Типа $cred = [System.Net.CredentialCache]::DefaultNetworkCredentials преобразовать в PSCredential ?

Оно по умолчанию так и работает, от имени текущего пользователя.

Ответить