PowerShell ile Dosya Hash Doğrulama ve Güvenlik Kontrolü

Dosya bütünlüğü kontrolü, sistem yöneticilerinin sıklıkla göz ardı ettiği ama aslında güvenlik stratejisinin tam merkezinde yer alması gereken bir konudur. Bir yazılım paketi indirdiniz, bir konfigürasyon dosyası sunucular arasında kopyalandı ya da kritik bir sistem dosyasının değiştirilip değiştirilmediğinden şüpheleniyorsunuz. İşte bu noktada hash doğrulama devreye girer ve PowerShell bu işi hem hızlı hem de son derece esnek biçimde yapmanıza olanak tanır.

Hash Nedir ve Neden Önemlidir

Hash, bir dosyanın içeriğini temsil eden sabit uzunlukta bir parmak izidir. MD5, SHA1, SHA256 gibi algoritmalar kullanılarak üretilen bu değer, dosyada tek bir bit bile değişse tamamen farklı bir sonuç verir. Bu özellik sayesinde:

  • İndirilen yazılım paketlerinin orijinalliğini doğrulayabilirsiniz
  • Kritik sistem dosyalarının yetkisiz değişime uğrayıp uğramadığını tespit edebilirsiniz
  • Sunucular arasında dosya transferinin hatasız tamamlanıp tamamlanmadığını kontrol edebilirsiniz
  • Adli bilişim süreçlerinde dosya kanıtlarının bütünlüğünü koruyabilirsiniz
  • Malware analizi sırasında şüpheli dosyaları tanımlayabilirsiniz

Windows ortamında PowerShell’in yerleşik Get-FileHash cmdlet’i bu işi neredeyse sıfır bağımlılıkla yapmanızı sağlar. Üçüncü parti araçlara ihtiyaç duymadan, mevcut altyapınızla güçlü bir dosya bütünlüğü kontrol sistemi kurabilirsiniz.

Get-FileHash Cmdlet’ine Giriş

PowerShell 4.0 ve sonrasında yerleşik olarak gelen Get-FileHash, kullanımı son derece sade bir araçtır. Temel sözdizimi şöyledir:

Get-FileHash -Path "C:dosya.exe" -Algorithm SHA256

Çıktı olarak üç alan döner:

  • Algorithm: Kullanılan hash algoritması
  • Hash: Hesaplanan hash değeri
  • Path: Dosyanın tam yolu

Desteklenen algoritmalar şunlardır:

  • SHA1: 160 bit, hızlı ama artık güvenli kabul edilmiyor
  • SHA256: 256 bit, günümüzde en yaygın kullanılan
  • SHA384: 384 bit, daha yüksek güvenlik gereksinimlerinde
  • SHA512: 512 bit, maksimum güvenlik
  • MD5: 128 bit, yalnızca bütünlük kontrolü için hâlâ kullanılıyor, güvenlik amacıyla önerilmez
  • MACTripleDES: Eski sistemlerle uyumluluk için
  • RIPEMD160: Alternatif 160 bit algoritması

Üretim ortamlarında SHA256 kullanmanızı şiddetle tavsiye ederim. MD5 ve SHA1’in çakışma açıkları (collision vulnerability) belgelenmiş durumda.

Temel Kullanım Örnekleri

Tek Dosya Hash Hesaplama

# SHA256 ile tek dosya
Get-FileHash -Path "C:WindowsSystem32cmd.exe" -Algorithm SHA256

# MD5 ile kontrol (eski sistemler için)
Get-FileHash -Path "C:tempsetup.exe" -Algorithm MD5

# Çıktıyı değişkene atama
$hash = Get-FileHash -Path "C:installerapp.msi" -Algorithm SHA256
Write-Host "Hash Degeri: $($hash.Hash)"

Birden Fazla Dosyayı Aynı Anda İşleme

# Bir dizindeki tüm exe dosyalarının hash'ini hesapla
Get-ChildItem -Path "C:WindowsSystem32" -Filter "*.exe" | 
    Get-FileHash -Algorithm SHA256 | 
    Select-Object Hash, Path |
    Export-Csv -Path "C:Raporlarsystem32_hashes.csv" -NoTypeInformation

# Belirli uzantıları filtrele
Get-ChildItem -Path "C:Apps" -Include "*.exe","*.dll","*.msi" -Recurse | 
    Get-FileHash -Algorithm SHA256

Hash Değerini Doğrulama

# Beklenen hash ile karşılaştırma
$expectedHash = "A1B2C3D4E5F6..."
$actualHash = (Get-FileHash -Path "C:downloadspackage.zip" -Algorithm SHA256).Hash

if ($actualHash -eq $expectedHash) {
    Write-Host "DOGRULANDI: Dosya butunlugu tamam" -ForegroundColor Green
} else {
    Write-Host "UYARI: Hash uyusmazligi tespit edildi!" -ForegroundColor Red
    Write-Host "Beklenen: $expectedHash"
    Write-Host "Bulunan : $actualHash"
}

Gerçek Dünya Senaryosu 1: İndirilen Yazılım Paketlerini Doğrulama

Yazılım tedarik zinciri saldırıları (supply chain attacks) son yıllarda ciddi bir tehdit haline geldi. SolarWinds vakasını hatırlıyorsunuzdur. Bu tür saldırıların önüne geçmenin ilk adımı, indirdiğiniz her paketin hash değerini üretici sitesindeki değerle karşılaştırmaktır.

Aşağıdaki fonksiyon bunu otomatikleştirir:

function Confirm-DownloadedFile {
    param(
        [Parameter(Mandatory=$true)]
        [string]$FilePath,
        
        [Parameter(Mandatory=$true)]
        [string]$ExpectedHash,
        
        [ValidateSet("MD5","SHA1","SHA256","SHA384","SHA512")]
        [string]$Algorithm = "SHA256"
    )
    
    # Dosya var mı kontrol et
    if (-not (Test-Path $FilePath)) {
        Write-Error "Dosya bulunamadi: $FilePath"
        return $false
    }
    
    Write-Host "Hash hesaplaniyor: $FilePath" -ForegroundColor Cyan
    $actualHash = (Get-FileHash -Path $FilePath -Algorithm $Algorithm).Hash
    
    # Kucuk/buyuk harf duyarsiz karsilastirma
    if ($actualHash.ToUpper() -eq $ExpectedHash.ToUpper()) {
        Write-Host "[OK] Dosya dogrulandi" -ForegroundColor Green
        Write-Host "Algoritma : $Algorithm"
        Write-Host "Hash      : $actualHash"
        return $true
    } else {
        Write-Host "[HATA] Hash uyusmazligi!" -ForegroundColor Red
        Write-Host "Beklenen  : $($ExpectedHash.ToUpper())"
        Write-Host "Hesaplanan: $actualHash"
        
        # Olay gunlugune yaz
        $logMessage = "$(Get-Date -Format 'yyyy-MM-dd HH:mm:ss') - HASH MISMATCH - Dosya: $FilePath"
        Add-Content -Path "C:Logssecurity_audit.log" -Value $logMessage
        
        return $false
    }
}

# Kullanim ornegi
$result = Confirm-DownloadedFile `
    -FilePath "C:Downloads7z2301-x64.exe" `
    -ExpectedHash "7ZRESMIHASHDEGERI" `
    -Algorithm SHA256

if (-not $result) {
    Write-Host "Kurulum iptal ediliyor!" -ForegroundColor Red
    exit 1
}

Gerçek Dünya Senaryosu 2: Kritik Sistem Dosyaları İzleme

Bir Windows Server ortamında kritik sistem dosyalarının değişip değişmediğini periyodik olarak kontrol etmek, hem güvenlik ihlallerini hem de kazara yapılan değişiklikleri tespit etmenizi sağlar. Bu senaryo özellikle PCI-DSS, ISO 27001 gibi uyumluluk gereksinimlerini karşılamak için de kullanışlıdır.

Önce bir temel (baseline) oluşturuyoruz:

function New-FileIntegrityBaseline {
    param(
        [string]$MonitorPath = "C:WindowsSystem32",
        [string]$BaselinePath = "C:Securitybaseline.json",
        [string]$Algorithm = "SHA256",
        [string[]]$Extensions = @("*.exe","*.dll","*.sys")
    )
    
    Write-Host "Baseline olusturuluyor: $MonitorPath" -ForegroundColor Yellow
    
    $baseline = @{}
    $fileCount = 0
    
    foreach ($ext in $Extensions) {
        $files = Get-ChildItem -Path $MonitorPath -Filter $ext -Recurse -ErrorAction SilentlyContinue
        
        foreach ($file in $files) {
            try {
                $hash = (Get-FileHash -Path $file.FullName -Algorithm $Algorithm -ErrorAction Stop).Hash
                $baseline[$file.FullName] = @{
                    Hash = $hash
                    LastModified = $file.LastWriteTime.ToString("yyyy-MM-dd HH:mm:ss")
                    Size = $file.Length
                    Algorithm = $Algorithm
                }
                $fileCount++
                
                if ($fileCount % 100 -eq 0) {
                    Write-Host "  $fileCount dosya islendi..." -ForegroundColor Gray
                }
            } catch {
                Write-Warning "Islenemedi: $($file.FullName) - $($_.Exception.Message)"
            }
        }
    }
    
    # JSON olarak kaydet
    $baselineData = @{
        CreatedAt = (Get-Date -Format "yyyy-MM-dd HH:mm:ss")
        ComputerName = $env:COMPUTERNAME
        MonitorPath = $MonitorPath
        FileCount = $fileCount
        Files = $baseline
    }
    
    $baselineData | ConvertTo-Json -Depth 10 | 
        Out-File -FilePath $BaselinePath -Encoding UTF8
    
    Write-Host "Baseline kaydedildi: $BaselinePath ($fileCount dosya)" -ForegroundColor Green
}

# Baseline olustur
New-FileIntegrityBaseline -MonitorPath "C:CriticalApp" -BaselinePath "C:Securityapp_baseline.json"

Şimdi bu baseline ile mevcut durumu karşılaştıran kontrol fonksiyonu:

function Compare-FileIntegrityBaseline {
    param(
        [string]$BaselinePath = "C:Securitybaseline.json",
        [string]$ReportPath = "C:Securityintegrity_report.txt",
        [string]$Algorithm = "SHA256"
    )
    
    if (-not (Test-Path $BaselinePath)) {
        Write-Error "Baseline dosyasi bulunamadi: $BaselinePath"
        return
    }
    
    $baselineData = Get-Content $BaselinePath -Raw | ConvertFrom-Json
    $report = @()
    $report += "=" * 60
    $report += "DOSYA BUTUNLUK KONTROL RAPORU"
    $report += "Tarih     : $(Get-Date -Format 'yyyy-MM-dd HH:mm:ss')"
    $report += "Baseline  : $($baselineData.CreatedAt)"
    $report += "Bilgisayar: $($baselineData.ComputerName)"
    $report += "=" * 60
    
    $changedFiles = @()
    $missingFiles = @()
    $newFiles = @()
    
    # Baseline'daki dosyalari kontrol et
    foreach ($entry in $baselineData.Files.PSObject.Properties) {
        $filePath = $entry.Name
        $originalData = $entry.Value
        
        if (-not (Test-Path $filePath)) {
            $missingFiles += $filePath
            continue
        }
        
        $currentHash = (Get-FileHash -Path $filePath -Algorithm $Algorithm -ErrorAction SilentlyContinue).Hash
        
        if ($currentHash -ne $originalData.Hash) {
            $changedFiles += [PSCustomObject]@{
                Path = $filePath
                OriginalHash = $originalData.Hash
                CurrentHash = $currentHash
                OriginalModified = $originalData.LastModified
            }
        }
    }
    
    # Sonuclari rapora ekle
    $report += ""
    $report += "DEGISEN DOSYALAR ($($changedFiles.Count) adet):"
    foreach ($f in $changedFiles) {
        $report += "  [DEGISTI] $($f.Path)"
        $report += "            Onceki Hash: $($f.OriginalHash)"
        $report += "            Yeni Hash  : $($f.CurrentHash)"
    }
    
    $report += ""
    $report += "EKSIK DOSYALAR ($($missingFiles.Count) adet):"
    foreach ($f in $missingFiles) {
        $report += "  [EKSIK] $f"
    }
    
    # Raporu kaydet
    $report | Out-File -FilePath $ReportPath -Encoding UTF8
    
    # Ozet ekrana yaz
    Write-Host "Degisen dosya: $($changedFiles.Count)" -ForegroundColor $(if($changedFiles.Count -gt 0){"Red"}else{"Green"})
    Write-Host "Eksik dosya  : $($missingFiles.Count)" -ForegroundColor $(if($missingFiles.Count -gt 0){"Red"}else{"Green"})
    Write-Host "Rapor kaydedildi: $ReportPath"
    
    # Kritik degisiklik varsa event log'a yaz
    if ($changedFiles.Count -gt 0 -or $missingFiles.Count -gt 0) {
        $eventMessage = "Dosya butunluk ihlali: $($changedFiles.Count) degisen, $($missingFiles.Count) eksik dosya"
        Write-EventLog -LogName Application -Source "FileIntegrityMonitor" `
            -EventId 9001 -EntryType Warning -Message $eventMessage `
            -ErrorAction SilentlyContinue
    }
}

Gerçek Dünya Senaryosu 3: Dosya Transferi Doğrulama

Büyük dosyaları sunucular arasında taşırken transfer sırasında bozulma olup olmadığını doğrulamak kritiktir. Özellikle WAN üzerinden yapılan transferlerde bu kontrol hayat kurtarıcı olabilir.

function Confirm-FileTransfer {
    param(
        [string]$SourcePath,
        [string]$DestinationPath,
        [string]$Algorithm = "SHA256"
    )
    
    Write-Host "Kaynak hash hesaplaniyor..." -ForegroundColor Cyan
    $sourceHash = Get-FileHash -Path $SourcePath -Algorithm $Algorithm
    
    Write-Host "Hedef hash hesaplaniyor..." -ForegroundColor Cyan
    $destHash = Get-FileHash -Path $DestinationPath -Algorithm $Algorithm
    
    $sourceSize = (Get-Item $SourcePath).Length
    $destSize = (Get-Item $DestinationPath).Length
    
    Write-Host ""
    Write-Host "Kaynak  : $SourcePath"
    Write-Host "Hedef   : $DestinationPath"
    Write-Host "Boyut K : $([math]::Round($sourceSize/1MB, 2)) MB"
    Write-Host "Boyut H : $([math]::Round($destSize/1MB, 2)) MB"
    Write-Host ""
    
    if ($sourceHash.Hash -eq $destHash.Hash) {
        Write-Host "[BASARILI] Transfer dogrulandi - Dosyalar ayni" -ForegroundColor Green
        Write-Host "Hash: $($sourceHash.Hash)"
        return $true
    } else {
        Write-Host "[BASARISIZ] Transfer hatasi - Dosyalar farkli!" -ForegroundColor Red
        Write-Host "Kaynak Hash: $($sourceHash.Hash)"
        Write-Host "Hedef Hash : $($destHash.Hash)"
        return $false
    }
}

# Kullanim
Confirm-FileTransfer `
    -SourcePath "\DC01sharebackup.zip" `
    -DestinationPath "D:LocalBackupsbackup.zip" `
    -Algorithm SHA256

Toplu Hash Veritabanı Oluşturma ve Şüpheli Dosya Tarama

Sistem yöneticilerinin sıklıkla ihtiyaç duyduğu bir senaryo: Belirli bir dizindeki tüm dosyaların hash’ini hesaplayıp dışa aktarmak ve daha sonra bu veritabanını şüpheli dosyaları tespit etmek için kullanmak.

# Tum kullanici profillerindeki executable dosyalari tara
# Bu, malware avlamak icin kullanisli bir tekniktir

function Invoke-MalwareScan {
    param(
        [string[]]$ScanPaths = @($env:USERPROFILE, $env:TEMP, "C:Users"),
        [string]$KnownBadHashesFile = "C:Securityknown_bad_hashes.txt",
        [string]$OutputReport = "C:Securitymalware_scan_$(Get-Date -Format 'yyyyMMdd_HHmmss').csv"
    )
    
    # Bilinen kotu hash listesini yukle
    $knownBadHashes = @{}
    if (Test-Path $KnownBadHashesFile) {
        Get-Content $KnownBadHashesFile | ForEach-Object {
            $knownBadHashes[$_.Trim().ToUpper()] = $true
        }
        Write-Host "Yuklenen kotu hash sayisi: $($knownBadHashes.Count)" -ForegroundColor Yellow
    }
    
    $results = @()
    $suspiciousCount = 0
    
    foreach ($scanPath in $ScanPaths) {
        if (-not (Test-Path $scanPath)) { continue }
        
        Write-Host "Tarama yapiliyor: $scanPath" -ForegroundColor Cyan
        
        $executableFiles = Get-ChildItem -Path $scanPath `
            -Include "*.exe","*.dll","*.bat","*.ps1","*.vbs","*.js" `
            -Recurse -ErrorAction SilentlyContinue
        
        foreach ($file in $executableFiles) {
            try {
                $hash = (Get-FileHash -Path $file.FullName -Algorithm SHA256 -ErrorAction Stop).Hash
                $isSuspicious = $knownBadHashes.ContainsKey($hash)
                
                if ($isSuspicious) { $suspiciousCount++ }
                
                $results += [PSCustomObject]@{
                    Path = $file.FullName
                    Hash = $hash
                    Size = $file.Length
                    Created = $file.CreationTime
                    Modified = $file.LastWriteTime
                    Suspicious = $isSuspicious
                }
                
                if ($isSuspicious) {
                    Write-Host "[TEHDIT] $($file.FullName)" -ForegroundColor Red
                    Write-Host "  Hash: $hash" -ForegroundColor Red
                }
            } catch {
                # Erisim engellenen dosyalar
            }
        }
    }
    
    $results | Export-Csv -Path $OutputReport -NoTypeInformation -Encoding UTF8
    
    Write-Host ""
    Write-Host "Tarama tamamlandi" -ForegroundColor Green
    Write-Host "Taranan dosya  : $($results.Count)"
    Write-Host "Suphe edilen   : $suspiciousCount" -ForegroundColor $(if($suspiciousCount -gt 0){"Red"}else{"Green"})
    Write-Host "Rapor          : $OutputReport"
}

Invoke-MalwareScan

Scheduled Task ile Otomatik Bütünlük Kontrolü

Bu kontrolleri manuel yapmak yerine zamanlanmış görev olarak çalıştırmak, proaktif bir güvenlik yaklaşımı sağlar:

# Integrity check script'ini zamanli gorev olarak kaydet
$taskAction = New-ScheduledTaskAction `
    -Execute "PowerShell.exe" `
    -Argument "-NonInteractive -ExecutionPolicy Bypass -File C:Securityintegrity_check.ps1"

$taskTrigger = New-ScheduledTaskTrigger `
    -Daily `
    -At "03:00AM"

$taskSettings = New-ScheduledTaskSettingsSet `
    -RunOnlyIfNetworkAvailable `
    -StartWhenAvailable `
    -MultipleInstances IgnoreNew

$taskPrincipal = New-ScheduledTaskPrincipal `
    -UserId "SYSTEM" `
    -LogonType ServiceAccount `
    -RunLevel Highest

Register-ScheduledTask `
    -TaskName "DosyaBütünlükKontrolu" `
    -TaskPath "Security" `
    -Action $taskAction `
    -Trigger $taskTrigger `
    -Settings $taskSettings `
    -Principal $taskPrincipal `
    -Description "Kritik sistem dosyalarinin gunluk butunluk kontrolu"

Write-Host "Zamanli gorev olusturuldu" -ForegroundColor Green

Performans İpuçları

Büyük dizinlerde hash hesaplaması zaman alabilir. Birkaç pratik ipucu:

  • Paralel işleme kullanın: PowerShell 7’de ForEach-Object -Parallel ile çok çekirdekli işlem yapabilirsiniz. Büyük dizinlerde ciddi hız kazanımı sağlar.
  • Önce boyut filtresi uygulayın: Hash hesaplamadan önce dosya boyutunu kontrol etmek, açıkça farklı dosyaları elek altına almanızı sağlar.
  • Incremental baseline güncellemeleri yapın: Her seferinde tüm dizini taramak yerine, LastWriteTime değeri değişen dosyaları kontrol edin.
  • SSD üzerinde çalışın: Hash hesaplaması I/O yoğun bir işlemdir. Mümkünse log ve baseline dosyalarını SSD’ye yazın.
  • Çıktıyı doğru formatta saklayın: Büyük hash veritabanları için CSV yerine SQLite veya basit bir hashtable tercih edin.

Sık Yapılan Hatalar

  • MD5’e güvenmek: MD5 bütünlük kontrolü için hâlâ işe yarasa da güvenlik amacıyla asla kullanmayın. Collision saldırılarına açıktır.
  • Hash’i güvensiz kanaldan almak: Eğer beklenen hash değerini indirdiğiniz aynı sunucudan alıyorsanız, sunucu ele geçirildiyse her iki değer de sahte olabilir. Hash değerini farklı kaynaklardan doğrulayın.
  • Sadece hash’e güvenmek: Hash değeri dosyanın içeriğinin değişmediğini söyler. Dosyanın zararlı olup olmadığını söylemez. Hash kontrolü, antivirüs ve diğer güvenlik katmanlarının yerini alamaz.
  • Baseline’ı güvensiz yerde saklamak: Eğer saldırgan baseline dosyanıza erişip değiştirebiliyorsa, kontrol mekanizmanız anlamsız hale gelir. Baseline’ları salt okunur veya ağdan izole bir lokasyonda saklayın.

Sonuç

PowerShell ile dosya hash doğrulama, Windows ortamınızda katmanlı güvenlik stratejisinin vazgeçilmez bir parçasıdır. Get-FileHash cmdlet’i tek başına bile son derece güçlüdür, ama bu yazıda gördüğümüz gibi fonksiyonlar ve scriptlerle birleştirildiğinde tam teşekküllü bir dosya bütünlük izleme sistemi oluşturabilirsiniz.

Pratikte önereceğim yol haritası şöyledir: Önce kritik uygulama dizinleriniz için bir baseline oluşturun. Bu baseline’ı ağdan izole, salt okunur bir konumda saklayın. Günlük zamanlanmış görevlerle karşılaştırma yapın ve sonuçları hem dosyaya hem de Windows Event Log’a yazdırın. Değişiklik tespit edildiğinde e-posta veya SIEM entegrasyonuyla alarm üretin.

Bu yaklaşım hem PCI-DSS Requirement 11.5 hem de CIS Benchmark kontrolleri gibi uyumluluk gereksinimlerini karşılamanıza da yardımcı olur. Ek bir maliyet olmadan, mevcut araçlarla profesyonel düzeyde dosya bütünlüğü izlemesi yapmak mümkün ve aslında oldukça kolaydır.

Yorum yapın