Skripte signieren
Um sicher zu stellen, dass ein Skript vertrauenswürdig ist, können Sie es signieren. Eine digitale Signatur ist eine überprüfbare "Unterschrift" des Programmierers, die es Powershell ermöglicht, die Herkunft des Skriptes und dessen Originalzustand zu prüfen.
Eine Signatur besteht aus einem Hash (einer Prüfsumme) über das gesamte Dokument, dermit dem privaten Schlüssel des Programmierers verschlüsselt ist. Hat man den öffentlichen Schlüssel des Programmierers, der in Form eines Zertifikats gespeichert und übertragen wird, kann man die Signatur entschlüsseln und die Echtheit prüfen sowie feststellen, ob das Skript seit der Erstellung der Signatur verändert wurde.
Sie als Programmierer benötigen für die Signatur zuerst einen privaten Schlüssel und das dazugehörige Zertifikat. Das bekommen Sie von einer Unternehmensinternen PKI (Public Key Infrastucture oder leicht vereinfacht gesagt einem Zertifikatsserver Ihres Unternehmens), einer öffentlichen Zertifizierungsstelle, die von Unternehmen wie Digicert oder Comodo betrieben wird, oder Sie stellen sich mit Powershell einfach eins selbst aus. Welches Zertifikat für Sie das richtige ist, hängt einzig davon ab, wer Ihren Skripten hinterher vertrauen soll. Für Firmeninterne Zwecke reicht eine Unternehmens-PKI, für öffentliche Skripte benötigen Sie ein Codesignaturzertifikat von einer öffentlichen Zertifizierungsstelle, und für unsere Testzwecke reicht ein selbstsigniertes Zertifikat.
Die Ausführungsrichtlinie "All signed"
Prinzipiell sind Powershell signierte Skripte eigentlich herzlich egal. Wenn als Ausführungsrichtlinie aber "All signed" bzw. "remote signed" ausgewählt wurde, muß ein Skript signiert sein, weil Powershell die Ausführung ansonsten verweigert. In Verbindung mit Applocker bzw. Application Control werden signierte Skripte damit zum vielleicht sichersten Weg, die Ausführung von Powershell-basiertem Schadcode vollständig zu unterbinden.
Zuerst erstellen wir dazu mit New-SelfSignedCertificate ein neues Codesignaturzertifikat. Genau genommen erstellt das Cmdlet nicht nur ein Zertifikat, sondern auch einen privaten Schlüssel, der in Ihrem Benutzerprofil gespeichert wird. Rufen Sie dazu folgendes Kommando auf:
$CertStore = 'Cert:\CurrentUser\'
$cert = New-SelfSignedCertificate -Type CodeSigningCert -Subject "CodeSignatur" -CertStoreLocation $CertStore\my
$CertFile = $cert | Export-Certificate -FilePath $env:TEMP\myCert.cer
Import-Certificate -FilePath $CertFile.FullName -CertStoreLocation $CertStore\Root
Import-Certificate -FilePath $CertFile.FullName -CertStoreLocation $CertStore\TrustedPublisher
Set-AuthenticodeSignature -Certificate $cert -FilePath c:\Skripte\SecureScript.ps1
In der ersten Zeile wird eine Variable deklariert, die auf den Zertifikatsspeicher des Benutzers verweist. In der nächsten Zeile wird im persönlichen Zertifikatsspeicher des Benutzers ein neues Codesignaturzertifikat erstellt.
Das Zertifikat ist selbstsigniert, was bedeutet, dass niemand diesem Zertifikat vertraut – nicht einmal Ihr eigener Rechner. Daher wir das Zertifikat einmal als Datei exportiert und die Datei anschließend wieder in den Speicher für vertrauenswürdigen Zertifizierungsstellen importiert. Ein direktes Kopieren des Zertifikats mit Powershell-Mitteln ist an dieser Stelle leider nicht möglich. Den Import müssen Sie außerdem auch noch einmal manuell bestätigen.
Nur, weil Ihr Rechner das Zertifikat jetzt kennt und prüfen kann, heißt das noch lange nicht, dass er ihm auch für Codesignaturen vertraut. Daher müssen Sie das Zertifikat zusätzlich in den Speicher für vertrauenswürdige Herausgeber importieren. Tun Sie das nicht, und Ihre Ausführungsrichtlinie ist auf "Allsigned" gestellt, fragt Powershell vor der ersten Ausführung, ob dem Herausgeber vertraut werden soll. Das ist eine manuelle Eingabe und in den meisten Fällen eher nicht erwünscht. Das gilt im Übrigen selbst für Zertifikate, die von einer vertrauenswürdigen Internet-Zertifizierungsstelle kommen. Möchten Sie ein Zertifikat automatisch in den Speicher für vertrauenswürdige Herausgeber verteilen, können Sie das mit Hilfe von Gruppenrichtlinien erledigen.
Anschließend können Sie das Zertifikat verwenden, um Skripte zu signieren – genau genommen verwenden Sie den privaten Schlüssel, der getrennt vom Zertifikat in Ihrem Benutzerprofil gespeichert ist, und geben das Zertifikat nur an, damit der korrekte private Schlüssel herausgesucht werden kann. Das Signieren geht dabei ganz einfach mit dem Cmdlet Set-AuthenticodeSignature, das von Ihnen nur die zu signierende Datei und das den privaten Schlüssel identifizierende Zertifikat wissen möchte.
Wenn Sie die signierte Datei nach dem Signieren öffnen, ist ein langer Kommentarblock an die Datei angehängt, der mit einer SIG-Zeile startet und mit einer SIG-Zeile endet.
# SIG # Begin signature block
# MIIFZAYJKoZIhvcNAQcCoIIFVTCCBVECAQExCzAJBgUrDgMCGgUAMGkGCisGAQQB
# gjcCAQSgWzBZMDQGCisGAQQBgjcCAR4wJgIDAQAABBAfzDtgWUsITrck0sYpfvNR
# …
# jpBaKiNfKacYjVo6jwz6etQE00ygH1e83wyf1SMKS/dMpDo0nF3tRg== # SIG # End signature block
Bekommen Sie eine Fehlermeldung Meldung "Unknown Error" beim Erstellen der Signatur, prüfen Sie, ob Ihr Zertifikat den Ordnern Root und den TrustedPublisher hinzugefügt wurde. Wenn das nicht passiert ist, weigert sich Powershell, die Datei zu signieren.
Noch mehr zu Ausführungsrichtlinien und Codesignaturen finden Sie unter https://www.hanselman.com/blog/SigningPowerShellScripts.aspx.