Windows PowerShell Protección del Shell

Don Jones

Cuando el equipo de Windows PowerShell se sentó a crear un shell nuevo y salió a relucir la palabra "scripting", seguramente los gritos de pánico se oyeron en todo Redmond. Después de todo, los esfuerzos anteriores de Microsoft sobre scripting administrativo (más concretamente me refiero a VBScript) no estuvieron exactamente libres de problemas. Un

modelo de ejecución excesivamente permisivo, combinado con la inclinación de la mayoría de usuarios a iniciar sesión como administradores completos, abrió la puerta al desastre.

Los participantes de esa primera reunión sobre Windows PowerShell™ seguramente se repitieron a ellos mismos eso de "de ninguna manera vamos a pasar por eso otra vez". Y lo han conseguido. Microsoft ha mejorado muchísimo su reputación en seguridad, en gran parte porque empezó a pensar en la seguridad en la fase inicial, en lugar de hacerlo cuando el ciclo de desarrollo de un producto ya estaba avanzado. Esta filosofía es mucho más evidente en Windows PowerShell.

¿Por qué no se ejecutan mis scripts?

Instale Windows PowerShell en un equipo recién instalado y haga doble clic en un archivo .ps1: se abrirá el Bloc de notas, no Windows PowerShell. Esto es porque la extensión del nombre de archivo .ps1 (la que usan los scripts de Windows PowerShell) no está asociada con el propio shell. Es decir, no se puede ejecutar un script sólo con hacer doble clic en él. La única manera de ejecutar un script es abrir Windows PowerShell, escribir el nombre de script y después presionar Intro.

En realidad, sólo con escribir el nombre del script tampoco es suficiente. Como puede ver en la figura 1, el archivo Demo1.ps1 existe en la carpeta actual, pero al escribir demo1 y presionar Entrar se genera el error siguiente: "El término 'demo1' no se reconoce como cmdlet, función, programa operable ni archivo de script." ¿A qué se debe? Después de todo, el archivo se encuentra ahí mismo.

Figura 1 Para evitar el secuestro de comandos, Windows PowerShell requiere que todos los scripts incluyan una ruta de acceso.

Figura 1 Para evitar el secuestro de comandos, Windows PowerShell requiere que todos los scripts incluyan una ruta de acceso.(Hacer clic en la imagen para ampliarla)

Este es otro aspecto del modelo de seguridad de Windows PowerShell. Una artimaña que normalmente intentan los usuarios malintencionados en otros shells es crear un script con el mismo nombre de archivo que un comando integrado. Así, por ejemplo, si deseara que un usuario ejecutara su script, podría llamarlo Dir.ps1 y dejarlo en una carpeta. Si convenciera al usuario para que escribiera Dir y después presionara Intro, se podría ejecutar su script, no el comando Dir que el usuario esperaba. Esta técnica se llama secuestro de comandos. En Windows PowerShell siempre es obligatorio proporcionar una ruta de acceso a ese script, protegiendo así bastante bien a Windows PowerShell frente a secuestros de comandos.

Ejecutar demo1 no funciona, porque no se incluye la ruta de acceso, pero ejecutar ./demo1 funciona. Esto es porque ahora he especificado una ruta de acceso, la carpeta actual. Esa línea de comandos es menos probable de ser confundida con un comando integrado, ya que si se estuviera refiriendo a un comando integrado nunca escribiría ningún tipo de ruta de acceso. Así, el hecho de requerir una ruta de acceso ayuda a que Windows PowerShell evite el secuestro de comandos y la confusión sobre lo que pueda suceder al presionar Intro.

Directiva para la ejecución de scripts

Así que tiene una copia recién instalada de Windows PowerShell, está usando la sintaxis apropiada e intenta ejecutar un script. Para su sorpresa, verá aparecer otro mensaje de error, que le informará que a Windows PowerShell no se le permite ejecutar scripts. ¿Qué? Bienvenido a la directiva de ejecución del shell.

Puede consultar cuál es la directiva de ejecución actual si ejecuta Get-ExecutionPolicy en el shell. De forma predeterminada, está establecida en Restricted, lo que resumiendo significa que los scripts no se ejecutarán. Nunca. Para nadie. De forma predeterminada, Windows PowerShell sólo se puede usar de manera interactiva, en lugar de para ejecutar scripts. Puede usar el cmdlet Set-ExecutionPolicy para elegir entre una de las cuatro configuraciones posibles de las directivas de ejecución:

  • Restricted, la configuración predeterminada, no permite ejecutar ningún script.
  • AllSigned sólo ejecuta scripts de confianza (más sobre ello en un momento).
  • RemoteSigned ejecuta scripts locales sin requerir que sean de confianza; sin embargo, los scripts descargados desde Internet se debe especificar que son de confianza antes de poder ejecutarlos.
  • Unrestricted permite ejecutar todos los scripts, incluso los que no son de confianza.

Francamente, AllSigned es la configuración más baja en la que debería establecerse cualquier equipo de producción. RemoteSigned resulta útil en entornos de desarrollo y pruebas, pero no es necesario para el usuario normal. Y no veo la necesidad de usar Unrestricted en ningún caso, y no me importaría si en versiones futuras de Windows PowerShell se omitiera esta configuración demasiado permisiva.

Es una cuestión de confianza

Su equipo está preconfigurado para confiar en ciertas entidades de certificación raíz (CA), es decir, servidores que publican certificados digitales. La figura 2 muestra la aplicación de panel de control Opciones de Internet, que muestra las entidades de certificación raíz de confianza. En Windows Vista®, esta lista es bastante corta y sólo incluye algunas entidades de certificación raíz comerciales importantes. Por el contrario, la lista predeterminada en Windows® XP es grande e incluye muchas entidades de certificación raíz de las que nunca he oído nada. Cuando una entidad de certificación raíz está en esta lista, esencialmente se está diciendo que se confía en que la empresa que usa la entidad de certificación raíz hará un buen trabajo al comprobar la identidad de alguien antes de emitir un certificado digital con su nombre.

Cuando se obtiene certificado digital de Clase III, que comúnmente se denomina un certificado de firma de código, la CA (que puede ser una CA comercial o privada que exista dentro de su organización) debe comprobar su identidad. Ese certificado digital es como un pasaporte o pieza de identidad electrónicos. Por ejemplo, antes darme un identificador que diga soy Don Jones, es necesario que la CA realice algunos pasos para asegurarse de que realmente sea Don Jones. Cuando usa su certificado para firmar digitalmente un script de Windows PowerShell, cosa que puede hacer con el cmdlet Set-AuthenticodeSignature, está firmando con su nombre en el script. Por supuesto, si puede obtener un certificado falso que contenga el nombre de otra persona, puede firmar con su nombre el script, y por eso es tan importante que sólo aparezcan CA de confianza en la lista de la figura 2.

Figura 2 CA de confianza predeterminadas en Windows Vista

Figura 2 CA de confianza predeterminadas en Windows Vista(Hacer clic en la imagen para ampliarla)

Cuando Windows PowerShell comprueba si se confía en un script, cosa que hace si la configuración de directiva de ejecución es AllSigned y RemoteSigned, hace tres preguntas:

  • ¿Está firmado este script? Si la respuesta es no, el script no es de confianza.
  • ¿Está intacta la firma digital? En otras palabras, ¿se ha cambiado el script desde que se firmó? Si la firma digital no está intacta, el script no es de confianza.
  • ¿Se creó la firma con un certificado digital publicado por una entidad de certificación raíz de confianza? Si la respuesta es no, el script no es de confianza.

CMDLET del mes: Set-AuthenticodeSignature

Set-AuthenticodeSignature es la clave para firmar digitalmente los scripts de Windows PowerShell, cosa que le permite usar la directiva de ejecución más segura del shell, AllSigned. Para usar este cmdlet, deberá empezar por otro (Get-ChildItem) que captura un certificado instalado de firma de código:

$cert = Get-ChildItem –Path cert:\CurrentUser\my –codeSigningCert

Necesita reemplazar la ruta de ese archivo por una que señale a un certificado instalado, cualquier certificado instalado será accesible mediante la unidad cert:. Cuando el certificado se haya cargado, ejecute esto para firmar un script:

Set-AuthenticodeSignature MyScript.ps1

–cert $cert

Debe proporcionar la ruta de acceso completa a su archivo de script .ps1. El shell agregará un bloque de firma digital al archivo.

¿En qué mejora la seguridad con la confianza? Con toda seguridad, un pirata informático podría escribir un script malintencionado, firmarlo y convencer a alguien de su entorno para que lo ejecute. Dado que el script estaría firmado, se ejecutaría. Pero dado que estaba firmado, podría usar la firma digital para buscar la identidad del autor y tomar las medidas pertinentes (como llamar a las fuerzas de orden público). Aunque alguien tendría que ser muy poco inteligente para crear un script malintencionado y después poner en él su nombre real.

Por supuesto, si un usuario malintencionado fuera capaz de obtener un certificado para la identidad de alguien distinto, por ejemplo de una CA que no tuviera un buen proceso de comprobación de identidades, no podría usar la firma digital para identificar al verdadero culpable. De ahí que las entidades emisoras raíz en las que confíe deben tener un proceso de comprobación de identidades que considere satisfactorio.

Si usa la configuración de directiva de ejecución AllSigned, deberá firmar digitalmente incluso los scripts que produzca usted mismo antes de poderlos ejecutar. El cmdlet Set-AuthenticodeSignature es bastante fácil de usar, pero aún puede representar una molestia. En este punto el software de terceros puede resultar práctico. Le recomiendo que seleccione un editor de scripts o un entorno de desarrollo visual que firmen automáticamente sus scripts con el certificado que especifique. De esta forma, el uso de firmas digitales resulta transparente y no requiere esfuerzos adicionales. Si desea ver sugerencias para editores y entornos de desarrollo que sean compatibles con esto, visite algunos de los foros de scripting (como el de mi sitio en ScriptingAnswers.com) y envíe un mensaje preguntando qué usan otros fanáticos de Windows PowerShell.

Cuando tenga un certificado, también puede ejecutar la línea siguiente para firmar todos los scripts de una ubicación determinada:

Get-Childitem *.ps1 | %{Set-AuthenticodeSignature $_.fullname $cert}

Protección centralizada

La directiva de ejecución de Windows PowerShell también se puede configurar individualmente, equipo por equipo. Pero esto no es una buena solución en entornos empresariales. En vez de eso, puede usar la Directiva de grupo. (Puede descargar las plantillas administrativas de Windows PowerShell desde go.microsoft.com/fwlink/?LinkId=93675.) Simplemente suelte el archivo en un Objeto de directiva de grupo (GPO) que afecte a todo su dominio y establézcalo en Restricted. De esta forma, no importa dónde esté instalado Windows PowerShell, podrá estar seguro que los scripts no podrán ejecutarse. A continuación, puede aplicar una configuración más permisiva (por ejemplo, AllSigned) en aquellos equipos que deban ejecutar scripts (como sería el caso de su propia estación de trabajo).

Credenciales alternativas

A diferencia de anteriores lenguajes de scripting administrativo para Windows, Windows PowerShell también está preparado para tratar con credenciales alternativas de forma segura. Por ejemplo, el cmdlet Get-WMIObject tiene un parámetro -credential que permite especificar una credencial alternativa para conexiones WMI remotas. El parámetro aceptará un nombre de usuario pero no una contraseña, impidiendo así la codificación de contraseñas privadas en un script de texto normal. En cambio, cuando proporcione el nombre del usuario, Windows PowerShell le preguntará la contraseña mediante un cuadro de diálogo. Si tiene pensado usar una credencial alternativa otra vez, puede almacenar la información de autenticación con el cmdlet Get-Credential:

$cred = Get-Credential DOMAIN\USER

Se le pedirá inmediatamente la contraseña y la credencial final se almacenará en la variable $cred. A continuación, la variable $cred se puede pasar al parámetro –credential tan a menudo como sea necesario. Incluso puede usar los cmdlets ConvertTo-SecureString y ConvertFrom-SecureString para convertir la credencial en una cadena cifrada o para convertir una cadena cifrada anteriormente de nuevo en credencial.

El problema de esto es que la clave de cifrado termina siendo almacenada en un script de texto normal, cosa que está completamente en contra de la seguridad. Así que en vez de hacer esto, puede agregar una llamada a Get-Credential en su perfil de Windows PowerShell. En ese caso, cuando Windows PowerShell se ejecuta, se le pide inmediatamente un nombre de usuario y una contraseña, que después se almacenan en una variable llamada $cred. De este modo, siempre tiene la variable $cred disponible en el shell que representa una cuenta de administrador del dominio. Y nunca tiene que preocuparse de almacenar esa credencial en ninguna parte; dado que la credencial se vuelve a crear cada vez que se abre el shell, no hay necesidad de almacenarla.

Seguridad predeterminada

De forma predeterminada, Windows PowerShell se instala y configura con los valores más seguros que pudiera sugerir para un shell administrativo que admita scripting. A menos que cambie alguna de esas configuraciones, Windows PowerShell permanecerá lo más protegido posible. Es decir, cualquier cosa que reduzca la seguridad de Windows PowerShell será resultado de su decisión y de sus acciones. Así que antes de cambiar alguno de los valores predeterminados (reconfigurando las asociaciones de las extensiones de archivo, cambiando la directiva de ejecución, etc.) asegúrese de entender completamente las consecuencias de sus acciones y esté preparado para responsabilizarse del resultado de sus cambios.

Don Jones es el gurú principal de scripting de SAPIEN Technologies y un instructor de ScriptingTraining.com. Puede ponerse en contacto con Don a través de su sitio web, ScriptingAnswers.com.

© 2008 Microsoft Corporation and CMP Media, LLC. Reservados todos los derechos; queda prohibida la reproducción parcial o total sin previa autorización.