Firmar scripts y endurecer ExecutionPolicy con AppLocker y WDAC

Última actualización: 17/12/2025
Autor: Isaac
  • La combinación de AppLocker/WDAC con ExecutionPolicy y firma de scripts permite controlar de forma granular qué código se ejecuta en los equipos.
  • Las políticas de ejecución no son un sistema de seguridad absoluto, pero reducen errores humanos y obligan a adoptar prácticas más seguras como AllSigned o RemoteSigned.
  • El uso de certificados de firma de código (idealmente desde una PKI corporativa) es clave para validar scripts en políticas estrictas y en despliegues a gran escala.
  • La seguridad real se consigue sumando varias capas: AppLocker/WDAC, ExecutionPolicy, permisos NTFS, GPO y una buena gestión de certificados y scripts.

Configuración de WDAC y AppLocker

En muchos entornos empresariales se sigue tirando de la solución rápida: bajar las defensas de PowerShell poniendo la ExecutionPolicy en Unrestricted y listo. Funciona, sí, pero abre la puerta de par en par a que cualquier usuario pueda lanzar scripts potencialmente peligrosos, tanto en servidores como en equipos de usuario.

La realidad es que Windows nos da herramientas muy potentes para hacer las cosas bien: firmar scripts, endurecer las políticas de ejecución y complementarlo con AppLocker o WDAC para controlar qué código se ejecuta en los equipos. Bien configurado, puedes tener automatización, flexibilidad y seguridad al mismo tiempo, sin ir a lo bruto.

AppLocker y WDAC: controlando qué scripts se ejecutan

AppLocker y su evolución, Windows Defender Application Control (WDAC), permiten definir qué scripts y binarios están autorizados a ejecutarse en tus sistemas. No sustituyen a las ExecutionPolicy de PowerShell, sino que las complementan como una capa más de defensa.

AppLocker trabaja con colecciones de reglas separadas (ejecutables, instaladores, scripts, aplicaciones empaquetadas, etc.), y en el caso concreto de scripts solo controla algunos tipos de archivo muy concretos. Esto es importante porque muchas organizaciones creen que “bloquean todo” y en realidad solo filtran unos pocos formatos.

En la colección de reglas de scripts, AppLocker solo contempla los siguientes formatos de archivo:

  • .ps1 – scripts de PowerShell
  • .bat – archivos por lotes clásicos de cmd
  • .cmd – otra variante de scripts por lotes
  • .vbs – scripts de VBScript
  • .js – scripts de JScript

Cuando habilitas AppLocker para scripts, si no creas ninguna regla de tipo “Permitir”, el comportamiento por defecto es muy claro: todo queda bloqueado salvo lo que las reglas autoricen explícitamente. Por eso es tan importante entender las reglas predeterminadas.

En una política típica, AppLocker ofrece una serie de reglas predeterminadas para la colección de scripts que se suelen usar como base:

  • Permitir a los administradores locales ejecutar todos los scripts
    Regla (por defecto): “Todos los scripts”
    Usuario: BUILTIN\Administrators
    Condición de ruta: *\ (equivale a cualquier ruta)
  • Permitir a todos los usuarios ejecutar scripts ubicados en la carpeta Windows
    Regla (por defecto): “Todos los scripts ubicados en la carpeta de Windows”
    Usuario: Todos
    Ruta: %windir%\*
  • Permitir a todos los usuarios ejecutar scripts de la carpeta Archivos de programa
    Regla (por defecto): “Todos los scripts ubicados en la carpeta Archivos de programa”
    Usuario: Todos
    Ruta: %programfiles%\*

Con este conjunto mínimo consigues que el sistema operativo y las aplicaciones instaladas funcionen sin romperse, pero evitas que los usuarios lancen scripts desde ubicaciones arbitrarias como el Escritorio o la carpeta Descargas.

Creación de reglas AppLocker mediante GPO

En entornos de dominio, lo habitual es gestionar AppLocker a través de Directivas de Grupo, lo que te permite aplicar el mismo control de ejecución de scripts a cientos o miles de equipos sin tener que tocar cada uno.

Para configurar AppLocker, se trabaja desde el Administrador de directivas de grupo en un controlador de dominio o en una consola RSAT. La ruta de la configuración de AppLocker es:

Ruta: Configuración del equipo → Directivas → Configuración de Windows → Configuración de seguridad → Directivas de control de aplicaciones → AppLocker

Dentro de AppLocker tendrás varias ramas: ejecutables, instaladores, scripts y aplicaciones empaquetadas. Para endurecer el uso de scripts, hay que centrarse en la colección de reglas de scripts y activar la aplicación de reglas haciendo clic en “Configurar la aplicación de reglas”.

Una vez habilitado, AppLocker se comporta con la lógica de lista blanca: bloquear por defecto y permitir solo lo definido. Para agilizar la tarea inicial, puedes usar el asistente de “Generar reglas automáticamente”, que analiza una carpeta y crea reglas de tipo Permitir basadas en:

  • Editor: identifica scripts firmados por un determinado editor (muy útil para software corporativo o de proveedores confiables).
  • Ruta: permite o bloquea scripts en rutas concretas (por ejemplo, C:\ScriptsSeguros\*).
  • Hash de archivo: se basa en la huella del archivo, de modo que, si este cambia, la regla deja de ser válida.

Esta generación automática ahorra muchísimo tiempo porque crea reglas de Permitir para el software ya instalado, reduciendo el riesgo de dejar sistemas inutilizables al activar AppLocker. Después puedes complementar con reglas manuales para rutas sensibles o scripts corporativos.

WDAC: versión reforzada del control de aplicaciones

Windows Defender Application Control (WDAC) es, por decirlo claro, el “AppLocker vitaminado” para escenarios más estrictos o modernos. Está pensado para entornos donde realmente quieres tener un modelo de lista blanca serio a nivel de binarios, drivers y scripts.

  Cómo Desactivar La Desfragmentación Automática En Windows 10

La lógica de WDAC es similar: solo se puede ejecutar aquello que una política explícitamente permite. Esto incluye EXE, DLL, scripts de PowerShell, MSIs, controladores, aplicaciones UWP, etc. Es mucho más granular y potente que AppLocker, pero también más complejo de desplegar.

Muchos administradores combinan WDAC con políticas de ejecución de PowerShell y con firma de scripts para construir un modelo de defensa por capas: el script tiene que estar permitido por WDAC/AppLocker y, además, cumplir la ExecutionPolicy. Si falla cualquiera de las dos, no se ejecuta.

Políticas de ejecución de PowerShell: qué son y qué no son

Las ExecutionPolicy de PowerShell generan mucha confusión porque mucha gente piensa que son una especie de “muro de seguridad infranqueable”. En realidad, la propia documentación oficial lo deja claro: son un mecanismo de seguridad ligero orientado a evitar errores humanos y ejecuciones accidentales, no un sistema antimalware.

La política de ejecución decide, de forma muy resumida, en qué condiciones PowerShell puede cargar scripts y archivos de configuración. Antes de ejecutar un .ps1, PowerShell revisa la política efectiva y el origen del archivo (local, remoto, descargado de Internet, etc.), y decide si lo permite, lo bloquea o muestra avisos.

Algunos beneficios reales de las políticas de ejecución son:

  • Reducen la probabilidad de que un usuario ejecute sin querer un script malicioso recibido por email o descargado de la web.
  • Fomentan prácticas de scripting más seguras, como la firma digital con certificados en políticas AllSigned o RemoteSigned.
  • Permiten aplicar reglas diferentes según el origen del script (local vs remoto), lo que hace la seguridad más flexible.
  • Se pueden combinar con GPO, AppLocker y WDAC para crear una política de control de código coherente.

Pero también hay que tener claras sus limitaciones:

  • Son fáciles de eludir para un usuario con privilegios, por ejemplo lanzando powershell.exe -ExecutionPolicy Bypass o usando otros lenguajes de scripting.
  • No analizan el contenido del script: un script firmado puede ser malicioso si el certificado se ha visto comprometido o el autor es malintencionado.
  • Solo afectan a PowerShell, no a VBScript, Python, binarios nativos, etc., por lo que no sustituyen a un control de aplicaciones global.
  • Una configuración excesivamente laxa (“Unrestricted” por sistema) crea una falsa sensación de seguridad, porque parece que “hay política” pero en la práctica no bloquea nada relevante.

Tipos de ExecutionPolicy en PowerShell

PowerShell define varios modos de política de ejecución que determinan qué scripts pueden ejecutarse y desde dónde. Es importante entender bien cada uno para no pasarse ni quedarse corto.

Principales tipos de ExecutionPolicy son:

Restricted

Es la política predeterminada en muchos clientes de Windows y significa que no se pueden ejecutar scripts de PowerShell en absoluto. Solo se permiten comandos introducidos de forma interactiva en la consola o ISE.

Esta política es adecuada para estaciones de trabajo o servidores en los que no se espere automatización. Eso sí, en cuanto necesites programar tareas o despliegues con scripts, tendrás que cambiarla o utilizar otra política a través de GPO.

RemoteSigned

Es la política predeterminada en muchos servidores Windows. Bajo este modo, los scripts creados localmente pueden ejecutarse sin firma, pero cualquier script descargado de Internet o recibido desde otra máquina debe estar firmado por un editor de confianza.

Esto se apoya en el mecanismo de Mark of the Web (MotW), que etiqueta los archivos descargados con información de zona. Si descargas un .ps1 de Internet y quieres ejecutarlo sin firmar, tendrás que desbloquearlo explícitamente con Unblock-File o quitar ese marcado.

AllSigned

Con esta política, todos los scripts y archivos de configuración, incluidos los creados localmente, deben ir firmados digitalmente por un certificado en el que confíe el sistema. Si alguno no lo está o la firma no es válida, no se ejecuta.

Además, normalmente se muestra una advertencia la primera vez que ejecutas un script firmado por un nuevo editor, para que el usuario pueda decidir si confía o no en ese certificado. Es la política ideal para entornos corporativos donde exista PKI y se quiera controlar de verdad quién puede publicar scripts.

Unrestricted

En esta política se pueden ejecutar todos los scripts sin firmas obligatorias. Los scripts remotos o descargados pueden mostrar una advertencia al usuario antes de ejecutarse, pero nada que un clic rápido no resuelva.

Es tentador usar esta política en entornos de desarrollo o en laboratorios porque “todo funciona a la primera”, pero en producción es una bomba de relojería. Debería evitarse en estaciones de trabajo y servidores reales, salvo casos muy justificados.

Bypass

En Bypass, PowerShell no aplica ninguna restricción ni muestra advertencias. Es como si no hubiera ExecutionPolicy, pensado sobre todo para procesos de automatización, orquestadores, integraciones con otras aplicaciones o pipelines de CI/CD donde ya exista otra capa de seguridad.

  Cómo diagnosticar errores en Windows con Dependency Walker: Guía completa

Usar Bypass como política global en equipos de usuario es básicamente renunciar a cualquier tipo de protección ante scripts. Si se usa, que sea de forma controlada, limitada a sesiones concretas o procesos muy específicos.

Undefined y Default

La opción Undefined indica que no hay una política explícita configurada en ese ámbito. Si todos los ámbitos están en Undefined, el comportamiento por defecto será Restricted en clientes Windows y RemoteSigned en servidores.

La opción Default suele usarse para volver al valor predeterminado del producto en ese sistema concreto, es decir, Restricted para cliente y RemoteSigned para servidor.

Ámbitos (Scopes) de la ExecutionPolicy y prioridad

La misma máquina puede tener varias políticas de ejecución definidas a la vez, cada una en un ámbito distinto. PowerShell resuelve cuál es la efectiva según una jerarquía de prioridad.

Los scopes principales son:

  • MachinePolicy: se define mediante GPO a nivel de equipo; se aplica a todos los usuarios de la máquina.
  • UserPolicy: también vía GPO, pero solo afecta al usuario concreto al que se aplica la directiva.
  • Process: solo se aplica a la sesión actual de PowerShell y vive en memoria; al cerrar la consola, desaparece.
  • LocalMachine: afecta a todos los usuarios del equipo y se guarda normalmente en el registro (HKLM).
  • CurrentUser: se aplica únicamente al usuario actual (HKCU).

La precedencia entre ámbitos es clave, porque explica por qué a veces tus cambios con Set-ExecutionPolicy “no hacen nada”:

  1. MachinePolicy (GPO equipo)
  2. UserPolicy (GPO usuario)
  3. Process
  4. LocalMachine
  5. CurrentUser

Esto significa que una política definida vía GPO siempre va a imponerse a lo que intentes cambiar localmente. Si MachinePolicy dice AllSigned y tú intentas poner RemoteSigned en LocalMachine, el sistema seguirá aplicando AllSigned.

Cómo ver y cambiar la ExecutionPolicy

Para comprobar qué política está realmente activa, conviene revisar todas las políticas por ámbito ejecutando:

Get-ExecutionPolicy -List

Este comando te muestra, de arriba abajo, la política en MachinePolicy, UserPolicy, Process, CurrentUser y LocalMachine, y cuál es la efectiva. Es la herramienta básica de diagnóstico cuando “algo no cuadra”.

Para cambiar la política en un ámbito concreto se usa Set-ExecutionPolicy. Algunos ejemplos típicos:

  • Establecer AllSigned a nivel de equipo:
    Set-ExecutionPolicy AllSigned -Scope LocalMachine -Force
  • Permitir scripts locales pero exigir firma a los remotos para el usuario actual:
    Set-ExecutionPolicy RemoteSigned -Scope CurrentUser -Force
  • Cambiar solo la sesión actual (útil para pruebas):
    Set-ExecutionPolicy Bypass -Scope Process -Force

Para modificar la política en el scope LocalMachine, es necesario abrir PowerShell como administrador. Si una GPO está definiendo MachinePolicy o UserPolicy, cualquier intento de cambio local en scopes inferiores devolverá un error indicando que la política está controlada por un Objeto de Directiva de Grupo.

Uso de GPO para forzar la política de ejecución

En redes corporativas lo habitual es centralizar estas configuraciones con GPO. Para ello se usa la directiva “Turn on Script Execution” (Activar la ejecución de scripts), disponible en:

Ruta: Configuración del equipo → Plantillas administrativas → Componentes de Windows → Windows PowerShell

Esta configuración permite:

  • Desactivar la ejecución de scripts por completo (equivalente a Restricted).
  • Habilitar la ejecución de scripts y escoger el tipo de política (por ejemplo, RemoteSigned o AllSigned).
  • No configurarla, en cuyo caso PowerShell utilizará las políticas locales (Set-ExecutionPolicy) sin que la GPO interfiera.

Una vez vinculada la GPO a la OU deseada, puedes forzar su aplicación en los clientes con gpupdate /force y verificar el resultado con Get-ExecutionPolicy -List. Verás cómo los valores de MachinePolicy o UserPolicy toman el control.

Firmar scripts de PowerShell: el enfoque profesional

En vez de tirar por la calle de en medio bajando la seguridad, lo razonable en una organización es dejar la ExecutionPolicy en AllSigned o RemoteSigned y firmar todos los scripts que vayan a circular por el dominio.

Para ello necesitas un certificado de firma de código. Hay dos caminos: usar una PKI corporativa (como ADCS) o, para pruebas, generar un certificado autofirmado. En un entorno serio de dominio, lo suyo es desplegar una CA interna y crear una plantilla específica de firma de código.

El flujo típico con ADCS es:

  • Instalar el rol de Servicios de Certificados de Active Directory en un servidor.
  • Crear o ajustar una plantilla de certificado de tipo Code Signing, definiendo qué usuarios o grupos pueden solicitarla (por ejemplo, administradores o equipo de DevOps).
  • Publicar la plantilla para que esté disponible en la CA.
  • Desde el equipo del administrador, abrir el complemento de certificados para el usuario actual (almacén personal) y solicitar un nuevo certificado de firma de código a la CA.
  • Distribuir el certificado raíz y, si procede, intermedios a todos los equipos mediante GPO, para que ese emisor sea de confianza en todo el dominio.

Una vez obtienes el certificado, puedes localizarlo en el almacén del usuario con:

  Activar o Desactivar la Tarjeta Gráfica Integrada desde el Administrador de Dispositivos de Windows

$cert = Get-ChildItem -Path Cert:\CurrentUser\My -CodeSigningCert

Con ese objeto certificado en la mano, firmar un script es tan sencillo como:

Set-AuthenticodeSignature -FilePath C:\Scripts\MiScript.ps1 -Certificate $cert

Si en vez de usar el almacén local tienes un .pfx exportado (por ejemplo, para firmar desde una máquina de build), se puede cargar con:

$cert = Get-PfxCertificate -FilePath C:\Certs\MiFirma.pfx
Set-AuthenticodeSignature -FilePath C:\Scripts\MiScript.ps1 -Certificate $cert

Para un entorno corporativo es muy recomendable incluir toda la cadena de certificación y una autoridad de sellado de tiempo, de forma que la firma siga siendo válida aunque el certificado caduque:

Set-AuthenticodeSignature -FilePath C:\Scripts\MiScript.ps1 -Certificate $cert -IncludeChain All -TimestampServer "http://timestamp.globalsign.com/scripts/timstamp.dll"

En cualquier momento puedes comprobar el estado de la firma de un script con:

Get-AuthenticodeSignature .\MiScript.ps1 | Format-List *

Si necesitas hacer pruebas rápidas sin una PKI real, PowerShell permite crear un certificado autofirmado de firma de código:

New-SelfSignedCertificate -FriendlyName "Ejemplo firma de código" -CertStoreLocation Cert:\CurrentUser\My -Subject "CN=FirmaScripts" -Type CodeSigningCert

Bypass y otros métodos para saltarse la ExecutionPolicy

Para que no haya engaños, conviene recordar que la política de ejecución se puede eludir de varias formas. Por eso no debe ser tu única medida de defensa, sino un complemento a AppLocker/WDAC, permisos NTFS y controles de endpoint.

El caso más típico es el de un sistema en Restricted o AllSigned en el que, por lo que sea, necesitas lanzar un script puntual no firmado. Si tienes permisos suficientes, puedes usar:

powershell.exe -ExecutionPolicy Bypass -File .\MiScript.ps1

o de forma abreviada:

powershell -ep Bypass .\MiScript.ps1

Esto lanza una nueva instancia de PowerShell solo para esa ejecución con la política Bypass, sin tocar la configuración global. Es cómodo, pero también el ejemplo perfecto de por qué las ExecutionPolicy, por sí solas, no frenan a un atacante con acceso interactivo.

También existe una variable de entorno especial, $env:PSExecutionPolicyPreference, que permite sobreescribir temporalmente la política en la sesión actual:

$env:PSExecutionPolicyPreference = "Bypass"

Mientras la variable esté establecida, PowerShell usará ese valor. Puedes comprobarlo con:

$env:PSExecutionPolicyPreference

Y cuando quieras volver al comportamiento normal, basta con eliminar la variable:

Remove-Item Env:PSExecutionPolicyPreference

Gestión de scripts no confiables y bloqueados

Cuando descargas un script de Internet, Windows suele marcarlo con Mark of the Web, lo que hace que ciertas políticas (como RemoteSigned) lo traten como script remoto aunque esté en disco local. El síntoma típico es el error de que “la ejecución de scripts está deshabilitada” o que “el archivo no está firmado digitalmente”.

Si has revisado el contenido y confías en él, puedes desbloquear el archivo sin cambiar la ExecutionPolicy usando:

Unblock-File -Path C:\Temp\ScriptDescargado.ps1

Una vez desbloqueado, y dependiendo de tu política (por ejemplo RemoteSigned), el script podrá ejecutarse si cumple el resto de requisitos. Esta es una manera más fina de trabajar que bajar la política a Unrestricted.

En entornos donde se use AllSigned o RemoteSigned de forma estricta, la gestión de certificados de confianza para editores externos también es importante. Si descargas un script firmado por un proveedor, tendrás que decidir si importas su certificado como editor confiable en el almacén adecuado, o si prefieres re-firmar el script con tu propio certificado interno.

Permisos NTFS y Group Policy como refuerzo

Además de AppLocker, WDAC y ExecutionPolicy, no hay que olvidar lo básico: los permisos NTFS sobre los directorios donde residen los scripts. Aunque un usuario pueda ejecutar un .ps1, no debería poder modificar scripts críticos que se ejecutan con privilegios altos.

Algunas buenas prácticas son:

  • Almacenar los scripts de producción en ubicaciones donde solo los administradores o un grupo de operadores tenga permisos de escritura.
  • Conceder a los usuarios finales solo permisos de lectura y ejecución cuando sea imprescindible.
  • Evitar que tareas críticas tiren de scripts ubicados en rutas manipulables como Escritorio, Descargas o perfiles de usuario.

Estas restricciones se pueden reforzar con GPO que definan ACLs en carpetas específicas, así como con políticas de AppLocker que solo permitan la ejecución de scripts desde rutas controladas y, a ser posible, firmados por tu CA corporativa.

Combinando todo esto, obtienes un escenario donde no basta con tener un .ps1 malicioso: el archivo debe estar en una ruta permitida, cumplir la ExecutionPolicy, pasar AppLocker/WDAC y, además, el atacante necesita permisos suficientes para colocarlo o modificarlo.

Trabajando con este enfoque por capas —firma de scripts, ExecutionPolicy endurecida, AppLocker/WDAC bien pensados, permisos NTFS correctos y algo de educación al usuario— es perfectamente viable tener automatización intensa con PowerShell sin convertir el entorno en el lejano oeste de los scripts.