Contents

Cómo gestionar las credenciales de un script de PowerShell

Una de las tareas comunes de la mayoría de scripts de automatización es la de logearse a servicios que requieren de autenticación. Cuando ésto ocurre nos encontramos con que de algún modo tenemos que gestionar las credenciales de dicho servicio, y en este punto podemos caer en la tentación de crear una magnífica variable $usuario y otra $password donde, en texto planto, almacenar las credenciales de nuestro servicio.

1
2
3
$usuario = 'usu4rio' 
$password = '3st03s1np4swrd' 
Connect-VIServer -Server vcsa.lab.local -User $usuario -Password $password

Esta es el típica configuración que durante la fase de desarrollo puede parecer la más práctica, pero que cuando llega el momento de poner el script en producción podemos ‘olvidar’ corregir. Si además lo juntamos con que estos scripts muchas veces se ejecutan con usuarios con permisos muy elevados (a menudo demasiado para la función que realizan) tenemos una bonita bomba que en cualquier momento nos puede explotar en la cara.

Así que no me enrollo más y pasemos a ver qué herramientas nos da PowerShell para gestionar las credenciales en nuestros scripts.

/wp-content/uploads/2019/03/Cómo-gestionar-las-credenciales-de-un-script-de-PowerShell.png
Cómo gestionar las credenciales de un script de PowerShell

Un vistazo a los Secure String

Los Secure Strings (System.Security.SecureString) como su nombre indica son datos de tipo string pero con un cifrado aplicado. Este cifrado, en su forma estandar, se realiza con DPAPI, que a grandes rasgos hace que dichos Secure Strings solo puedan ser descifrados por el propio usuario que los generó y en la misma máquina que lo hizo.

En este punto ya podemos ver por donde va la película: si introducimos un password en formato Secure String después seremos capaces de almacenarlo para su próxima ejecución y si alguien se apropia del archivo no será capaz de utilizar el password que hay almacenado en el.

Utilizando Get-Credential para gestionar las credenciales

La forma más sencilla de gestionar las credenciales en PowerShell es utilizando el cmdlet Get-Credential. Si lo ejecutamos veremos que se nos pedirá un usuario y contraseña:

/wp-content/uploads/2018/06/Get-Credential.png
Gestionar las credenciales con Get-Credential

Para seguir con el ejemplo del principio del post llenaremos la credencial con los mismos valores:

/wp-content/uploads/2018/06/Get-Credential-rellenado.png
Get-Credential rellenado

Si ahora examinamos el contenido de nuestra variable $credenciales veremos que se trata de un objeto de tipo System.Management.Automation.PSCredential con las propiedades ‘UserName’ y ‘Password’:

/wp-content/uploads/2018/06/Contenido-objeto-credencial.png
Contenido objeto credencial

Si nos fijamos bien la propiedad ‘Password’ es un Secure String, por lo que si intentamos mostrar la propiedad no la veremos a menos que la volvamos a convertir a texto planto con ConvertFrom-SecureString:

/wp-content/uploads/2018/07/Contenido_securestring.png
Contenido Secure String

Que como podemos ver nos devuelve el string cifrado.

Guardando las credenciales con Export-Clixml

Vale, ya tenemos nuestro objeto de credenciales creado, pero ahora la idea sería poder guardarlo de algún modo para una futura utilización. Para ello, una buena forma es valerse del cmdlet Export-Clixml, que crea una representación de un objeto con todas sus propiedades dentro de un archivo XML.

1
$credenciales | Export-Clixml -Path C:\Export\credenciales.xml

Si abrimos el contenido del archivo XML veremos algo así:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
<Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04">
  <Obj RefId="0">
    <TN RefId="0">
      <T>System.Management.Automation.PSCredential</T>
      <T>System.Object</T>
    </TN>
    <ToString>System.Management.Automation.PSCredential</ToString>
    <Props>
      <S N="UserName">usu4rio</S>
      <SS N="Password">01000000d08c9ddf0115d1118c7a00c04fc297eb010000007ac4a7fc58e25c4b8481520c37cb9c8800000000020000002000106600002001000220000000ca81c7eb68d072e1a7a7e8ce250920bf5c3bcbb99faac9275cc967ad71cb77b8000000000e8000000002000020000000f8093b49fa4ade062a9cd1bb98641b60dd1bf2f08c18c40483a09eed0cccda9a200000200603d4d7e00ca4a893b80da7ee28702adc89029b43302cfa29ec5c81bcc4f96040000000f4feb94583668fb08070898ead1c162fdde71995616c1e245ac72ca55c0d963e4800f447a54e05b45de2827427757b5da5bd552712575e3907d15f6d6f3c17dd</SS>
    </Props>
  </Obj>
</Objs>

Importando y utilizando las credenciales almacenadas

Ya llegamos al final del asunto. Ahora que tenemos las credenciales almacenadas solo nos queda ver como podemos utilizarlas. Aún con el ejemplo del principio del post podríamos usar las credenciales así:

1
2
$credenciales = Import-Clixml -Path C:\Export\credenciales.xml
Connect-VIServer -Server vcsa.lab.local -Credential $credenciales

Como podéis ver en vez de utilizar los parámetros -User y -Password utilizamos el parámetro -Credential. La mayoría de cmdlets que autentican contra otros servicios admiten este parámetro, que espera un objeto PSCredential para realizar la autenticación en vez de pedir por separado usuario y password. ¿Quiere decir eso que si el cmdlet no admite la entrada de objetos PSCredential nos vemos obligados a utilizar usuario y contraseña en texto plano?

1
2
$credenciales = Import-Clixml -Path C:\Export\credenciales.xml 
Connect-VIServer -Server vcsa.lab.local -User $credenciales.UserName -Password $credencial.GetNetworkCredential().password

Evidentemente no, los objetos PSCredential disponen de un método con el que podemos acceder a las credenciales descifradas, con lo que podríamos acceder al password tal y como vemos en el ejemplo con $credencial.GetNetworkCredential().password, así que no hay excusa para seguir utilizando credenciales “hardcodeadas” en nuestros scripts.

Conclusión

¿Utilizando este método estamos totalmente protegidos? Realmente no, porque como hemos visto el cifrado reversible está vinculado al usuario que ha generado el secure string, por lo que si nuestra cuenta queda vulnerada (o una cuenta de administrador de nuestra máquina que pueda resetear nuestro password) podremos tener un problema, pero desde luego reduce el vector de ataque notablemente y puede evitar que por un despiste se filtre nuestro script y con ello las credenciales que éste utilizaba.