2.02.2023

Powershell betiklerini elektronik olarak imzalamak

Müthiş bir koruma yöntemi olmasa da powershell'de varsayılan olarak betiklerin çalıştırılmalarını engelleyen bir politika var. Yani yeni bir Windows bilgisayar aldınız/kurdunuz. Varsayılan olarak powershell terminalinden sadece komutlar girebilirsiniz. ps1 uzantılı betik dosyalarını çalıştırmanız engellenir. Eğer bu engeli kaldırmak isterseniz Set-ExecutionPolicy ile çalıştırma politikanızı değiştirmelisiniz. Şu çalıştırma seviyeleri vardır:

AllSigned: Tüm betikler imzalı olsun.

ByPass: Yasakları takma

RemoteSigned: Sadece download edilmiş betiklerin imzalı olmalarını bekle. Yerel oluşturulmuş betiklerin imzalı olmasını bekleme (bir dosyanın nasıl download edilmiş olduğunu anlarız? ZoneIdentifier ile).

Restricted: Tüm betikleri engelle.

Default: Varsayılan olarak iş istasyonları için restircted, sunucular için remotesigned.

Undefined: Politika tanımlı değil, default uygulanır. Niye ayrıca buna ihtiyaç duyulmuş, bilmiyorum.

Unrestircted: Patron çıldırdı, herşey serbest!

Amacımız Execution Policy'yi AllSigned yapabilmek için tüm scriptleri imzalamak için gereken adımları özetlemek. Önce bir sertifikaya ihtiyacımız var. Sertifka edinmenin pek çok yolu var. Bir sertifika hizmeti veren firmadan para ile almak bir yöntem. Yerel ağımızda bize bu hizmeti verecek bir sunucu (CA, certificate authority) varsa oradan da edinebiliriz. Bunların hiçbiri yoksa, sadece deneme amaçlı kullanılması önerilen "kendi kendine imzalanmış" bir sertifika da oluşturabiliriz. Sonuçta Windows Sertifika Deposu'nda bulunan ve kod imzalamak amacıyla üretilmiş (bu zorunlu) bir sertifikamız olsun. Buna bir PSDrive olan cert: sürücüsünden şu şekilde erişip bir değişkene atayabilriz:

PS> $sertifika = (Get-ChildItem cert:\CurrentUser\My -CodeSigningCert)[0]

Bu konumda birden fazla varsa ilkini seçer. Eğer illa "kendi kendine imzalanmış" sertifikamız olsun diyorsak

PS> New-SelfSignedCertificate -Subject "CodeSigning" -CertStoreLocation cert:\CurrentUser\My -Type CodeSigningCert -NotAfter (Get-Date).AddYears(5) -Keylength 4096 -Friendlyname PowershellSignature

Burada gözüken adımız "CodeSigning" olacak, sertifika cert:\CurrentUser\My (yani geçerli kullanıcının kişisel sertifikalar bölümünde) saklanacak, sertifikanın tipi CodeSigninCert olacak, 5 yıl geçerliliğe sahip olacak, anahtar uzunluğu 4096 bit olacak ve ismi PowershellSignature olacak. Eğer bunu kullancaksak ve geçerli kullanıcının kişisel sertifikalarında başka sertifika yoksa bunu bir değişkene atamak için yapacağımız şu olacaktır:

PS> $sertifika = Get-ChildItem cert:\CurrentUser\My -CodeSigningCert

Sonra Set-AuthenticodeSignature cmdlet'ini kullanarak ps1 uzantılı dosyamızı imzalayalım:

PS>  Set-AuthenticodeSignature -Filepath C:\scripts\sample.ps1 -Certificate $sertifika

İmzalanan powershell dosyasının sonuna yorum için kullanılan "#" karakteri ile başlayan 47 satırlık bir hash bölümü eklenecek. Bu hash, dosyamızın imzası olacaktır. İmzalandıktan sonra dosyada bir değişiklik olursa dosyayı tekrar imzalamamız gerekecektir.

Set-AuthenticodeSignature'ı çalıştırdıktan sonra Status kısmında UnknownError görürsek bu, işlemi yapmak için yükseltilmiş ayrıcalıklara ihtiyacımız olduğunu gösterir. Muhtemelen normal kullanıcı haklarımızla CurrentUser değil de LocalMachine altından bir sertifika okumuşuzdur. Normal yetkilerimizle işlem yapmak için sertifikayı CurrentUser altına koymak daha pratik bir yol.

Normal şartlar altında bir sertifikaya güvenebilmek için o sertifikayı imzalayan (bize veren) kurumun kök sertifikasına da güvenmemiz gerek. Yani o da bizim sertifika depomuzda olmalı. "kendinden imzalı" sertifikalar için böyle birşey olmadığından sertifika zincirlerinde bir güvensizlik sarı bir ünlem veya kırmızı çarpı ile kendini belli edebilir. Bu durumun kolay çözümü "kendinden imzalı" sertifikamızın bir koyasını da "Güvenilir Kök Sertifika Yetkilileri" (Trusted Root Certification Authorities) koymaktır.

Son olarak execution policy'mizi AllSigned yapabiliriz, ama bunu yapmak için yine yükseltilmiş ayrıcalıklara (yönetici yetkilerine) ihtiyacımız olacak:

PS> Set-ExecutionPolicy AllSigned

Betiği her değiştirdiğimizde bu sürece tekrar katlanmamak için bu işi bir de yapacak bir betik yazabiliriz (hata denetimini atladım). İmzalanacak dosyayı -filename parametresi ile belirtmek gerek. Bu dosyayı oluşturduktan sonra çalıştırabilmek için yukarıdaki satırları bir kez daha kullanarak bunu da imzalamamız gerek.

function imzala {
    param(
        [string]$filename 
    )

    $s = Get-ChildItem cert:\CurrentUser\My -CodeSigningCert
    Set-AuthenticodeSignature -Filepath $filename -Certificate $s
}

Hiç yorum yok: