Firmar scripts de PowerShell
Yo ❤️ PowerShell. Es una herramienta realmente útil para automatizar esas tareas que haces varias veces. Pero también puede ser un enorme agujero de seguridad si se lo permites.
PowerShell permite administrar casi todo lo que hay en tu máquina, por lo que es mucho el daño que podría hacer alguien capaz de ejecutar scripts maliciosos en tus entornos.
Antes se limitaba sólo a Windows, desde la versión 6 y ahora con el lanzamiento de PowerShell 7.0, también se puede implementar en Linux y MacOS. Sin embargo este artículo habla de Políticas de Ejecución que no pueden ser cambiadas en entornos no Windows por lo que no proporcionará ningún beneficio a los usuarios de Linux/MacOS (lo siento).
Políticas de Ejecución
Una forma de restringir la capacidad de ejecutar scripts en sus entornos Windows es utilizar las políticas de ejecución de PowerShell. El tldr es que se pueden utilizar para restringir los scripts que se ejecutarán en el entorno. Las opciones disponibles desde la menos segura a la más segura son:
Superar
No se bloquea nada.
Sin restricciones (Siempre se aplica en equipos que no son Windows)
Igual que ByPass
pero solicita al usuario antes de ejecutar scripts desde Internet.
RemoteSigned (por defecto para servidores Windows)
Los scripts que se han descargado de Internet sólo pueden ejecutarse si están firmados. Los scripts escritos en la máquina pueden ser ejecutados
AllSigned
Todos los scripts deben ser firmados antes de ser ejecutados
Restricted (por defecto para Clientes Windows)
Impide la ejecución de scripts. Sólo puede ejecutar comandos individuales.
Ejecución de scripts
Así que quieres ejecutar tus propios scripts de PowerShell en tu servidor. Pero te aparece un PSSecurityException
como el siguiente.
PS C:\Users\Administrator\Downloads> .\wibble.ps1.\wibble.ps1 : File .\wibble.ps1 cannot be loaded. The file.\wibble.ps1 is not digitally signed. You cannot run this script on the current system.For more information about running scripts and setting execution policy, see about_Execution_Policies athttp://go.microsoft.com/fwlink/?LinkID=135170.At line:1 char:1+ .\wibble.ps1+ ~~~~~~~~~~~~ + CategoryInfo : SecurityError: (:) , PSSecurityException + FullyQualifiedErrorId : UnauthorizedAccess
Una forma de resolver este problema es cambiar la política de ejecución utilizando el comando Set-ExecutionPolicy
y reducir la seguridad de la máquina. Esto es una mala idea en general.
Personalmente, nunca usaría nada menos seguro que RemoteSigned
en un servidor, pero AllSigned
idealmente.
¿Qué es una firma de script?
Supuestamente quieres mantener la seguridad de la máquina y no abrir una vulnerabilidad así que la opción que te queda es firmar tus scripts.
Firmar un script significa que alguien con acceso a la clave privada de un certificado de firma de código ha añadido un bloque de firma al final del archivo de script.
function Get-Wibble { return "Wibble"}# SIG # Begin signature block# vpnIUpm2XxLRhU1no0iuA62xKxYzR6m95z9Ax21ppeTC9NoRd8ocoSGr1zAd# qlMOlz4lZoVWR4ZmtdCgzde1dVxzv4jjHb6ziDiY2o05UXswD2bl6XaOrUpd# Li0Qjg3d3y2r1nrpO8hos906bgXQswysvouegUJcpt8ftmqBKfEYNeBgnBFm# SIG # End signature block
(Sólo como ejemplo. El bloque de firma suele ser mucho más largo)
El bloque de firma contendrá un hash del script que ha sido encriptado utilizando la clave privada. Cuando se intenta ejecutar el script se descifra utilizando la clave pública y se compara con el hash real. Si coincide, podemos confirmar que el script no ha sido manipulado, ya que el hash cambiaría en cuanto eso ocurriera.
Certificados de autofirma
Si sus scripts sólo van a ser ejecutados por máquinas de su organización, lo más probable es que pueda autofirmar los certificados. La alternativa es gastar $$$ y comprar un certificado de firma de código cada año.
Autofirma significa que generará el certificado usted mismo y firmará los scripts usando eso.
Cómo crear un certificado autofirmado
PowerShell tiene el muy útil comando New-SelfSignedCertificate
para producir certificados autofirmados. Luego podemos exportarlos y colocarlos en diferentes almacenes.
Por defecto el comando creará un certificado que caduca al cabo de 1 año. Puedes cambiar esto usando el parámetro -NotAfter
y proporcionándole la fecha en la que deseas que el certificado expire.
Para crear un certificado dispara una sesión de PowerShell y ejecuta lo siguiente
$CertificateName = Read-Host "Input your certificate name"$OutputPFXPath = "$CertificateName.pfx"$OutputCERPath = "$CertificateName.cer"$Password = Get-Credential -UserName Certificate -Message "Enter a secure password:"$certificate = New-SelfSignedCertificate -subject $CertificateName -Type CodeSigning -CertStoreLocation "cert:\CurrentUser\My"$pfxCertificate = Export-PfxCertificate $certificate -FilePath $OutputPFXPath -password $Password.passwordExport-Certificate -Cert $certificate -FilePath $OutputCERPathImport-PfxCertificate $pfxCertificate -CertStoreLocation cert:\CurrentUser\Root -Password $password.passwordWrite-Output "Private Certificate '$CertificateName' exported to $OutputPFXPath"Write-Output "Public Certificate '$CertificateName' exported to $OutputCERPath"
Esto creará el certificado y lo exportará a dos archivos separados con las extensiones .pfx
y .cer
.
PFX
El certificado PFX es el que habrá que instalar en las máquinas que van a realizar la firma. En nuestro caso es cada una de las máquinas de nuestros desarrolladores.
Debe instalarse en el almacén cert:\CurrentUser\Root
, también conocido como almacén de «Autoridades de Certificación de Raíz de Confianza».
Este archivo debe guardarse en algún lugar seguro. La contraseña debe asegurarse por separado (utilice un gestor de contraseñas). Si un mal actor se apodera de ambos, puede firmar sus scripts maliciosos como si fuera usted.
CER
El archivo de certificado CER es lo que va a tener que instalar en las máquinas que ejecutarán los scripts. Esto no requerirá una contraseña para instalar, pero no se puede utilizar para firmar las secuencias de comandos. Sólo contiene la clave pública utilizada para descifrar la firma.
Debido a que no contiene la clave privada este certificado puede ser distribuido libremente a todos los entornos que ejecutarán los scripts.
Esto tendrá que ser instalado en dos almacenes de certificados en las máquinas que ejecutarán los scripts; Los almacenes cert:\LocalMachine\Root
y cert:\LocalMachine\TrustedPublisher
.
Firmar un script
La función Set-AuthenticodeSignature
se proporciona para firmar los scripts. Necesitamos pasar la ruta al script y el certificado del almacén. Escribimos una función wrapper que puede firmar los scripts.
function Set-ScriptSignatures { param ( $pathToScripts = "." ) $certificateName = "PowerShell Signing Certificate" $scripts = Get-ChildItem -Path "$pathToScripts\*" -Include *.ps1, *.psm1 -Recurse $certificate = @(Get-ChildItem cert:\CurrentUser\My -codesign | Where-Object { $_.issuer -like "*$certificateName*" } ) foreach ($script in $scripts) { Write-Host "Signing $script" Set-AuthenticodeSignature $script -Certificate $certificate }}
La variable $certificateName
debe contener el nombre utilizado al crear el certificado.
Puede utilizar la función proporcionando una ruta al directorio que contiene los scripts y hará un bucle a través de ellos firmándolos todos.
Una vez que haya instalado los scripts en la máquina de destino ahora debería ser capaz de ejecutar los scripts sin ningún problema.