IIS Güvenlik Sertleştirme: En İyi Pratikler ve Yapılandırma Rehberi
Üretim ortamında IIS kurduğunuzda, Microsoft’un varsayılan ayarları sizi çalışır hale getirir ama güvende tutmaz. Yıllar içinde gördüğüm en büyük sorun şu: sysadminler IIS’i kurup uygulamayı yayınlıyor, sonra bir daha dönüp bakmıyor. Ta ki bir pentest raporu gelene ya da loglardan anormal trafik görülene kadar. Bu yazıda IIS güvenlik sertleştirmesini adım adım ele alacağız. Teoriden çok, gerçekten uyguladığım ve işe yarayan şeylerden bahsedeceğim.
Neden IIS Sertleştirmesi Kritik?
IIS, Windows Server ortamlarında .NET uygulamaları, legacy ASP uygulamaları ve çeşitli web servislerini barındırmak için hâlâ yaygın olarak kullanılıyor. Özellikle kurumsal Windows ekosistemlerinde kaçınılmaz bir bileşen. Ancak “Windows Server var, Defender var, firewall var, ne olabilir ki?” düşüncesi tehlikeli bir yanılgı.
Varsayılan IIS kurulumu şunları açık bırakır:
- Sürüm bilgisi ifşası: Response header’larında IIS ve .NET versiyonunuz görünür
- Gereksiz HTTP metodları: WebDAV, TRACE, OPTIONS herkes tarafından kullanılabilir
- Directory browsing: Yanlış yapılandırılmış siteler dizin listesi verir
- Zayıf TLS konfigürasyonu: Eski protokoller ve şifre takımları aktif kalır
- Varsayılan hata sayfaları: Stack trace ve sunucu bilgisi dışarıya sızar
Bunları düzeltmek için hem PowerShell hem de appcmd.exe araçlarını kullanacağız. IIS Manager GUI ile de yapılabilir elbette, ama otomasyon için komut satırı şart.
Versiyon Bilgisi İfşasını Kapatmak
Bir saldırganın ilk yaptığı şey keşif aşamasıdır. curl -I https://siteniz.com komutuyla response header’larına bakan biri şunu görür:
Server: Microsoft-IIS/10.0
X-Powered-By: ASP.NET
X-AspNet-Version: 4.0.30319
Bu üç satır, saldırgana hangi açıkları arayacağını söyler. Hepsini kapatmak için:
# Server header'ını gizle
Import-Module WebAdministration
Set-WebConfigurationProperty -pspath 'MACHINE/WEBROOT/APPHOST' `
-filter "system.webServer/security/requestFiltering" `
-name "removeServerHeader" -value "true"
# X-Powered-By header'ını kaldır
Remove-WebConfigurationProperty -pspath 'MACHINE/WEBROOT/APPHOST' `
-filter "system.webServer/httpProtocol/customHeaders" `
-name "." -AtElement @{name='X-Powered-By'}
X-AspNet-Version header’ı için web.config veya applicationHost.config dosyasına şunu ekleyin:
<system.web>
<httpRuntime enableVersionHeader="false" />
</system.web>
Bu değişikliklerden sonra response header’larında sunucu hakkında hiçbir bilgi kalmaz. Basit ama etkili.
HTTP Metodlarını Kısıtlamak
Çoğu web uygulaması sadece GET ve POST kullanır. TRACE metodu ise Cross-Site Tracing (XST) saldırılarına kapı açar, WebDAV metodları ise yanlış yapılandırılmış sunucularda dosya yükleme vektörü oluşturur.
# Sadece GET ve POST metodlarına izin ver, diğerlerini engelle
$filter = "system.webServer/security/requestFiltering/verbs"
# Önce mevcut yapılandırmayı kontrol edelim
Get-WebConfiguration -pspath 'MACHINE/WEBROOT/APPHOST' -filter $filter
# TRACE metodunu engelle
Add-WebConfigurationProperty -pspath 'MACHINE/WEBROOT/APPHOST' `
-filter $filter `
-name "." -value @{verb='TRACE'; allowed='false'}
# OPTIONS metodunu engelle
Add-WebConfigurationProperty -pspath 'MACHINE/WEBROOT/APPHOST' `
-filter $filter `
-name "." -value @{verb='OPTIONS'; allowed='false'}
# PUT metodunu engelle (WebDAV kapalıysa zaten gelmiyor ama yine de)
Add-WebConfigurationProperty -pspath 'MACHINE/WEBROOT/APPHOST' `
-filter $filter `
-name "." -value @{verb='PUT'; allowed='false'}
# DELETE metodunu engelle
Add-WebConfigurationProperty -pspath 'MACHINE/WEBROOT/APPHOST' `
-filter $filter `
-name "." -value @{verb='DELETE'; allowed='false'}
REST API barındırıyorsanız PUT ve DELETE metodlarına ihtiyacınız olabilir. Bu durumda bu metodları sadece ilgili site veya uygulamanın web.config dosyasında açık tutun, global olarak değil.
TLS Yapılandırması: SSL/TLS Sertleştirmesi
Bu bölüm IIS sertleştirmesinin en kritik parçası. TLS 1.0 ve TLS 1.1 artık kabul edilemez. POODLE, BEAST, CRIME gibi saldırılar bu protokolleri hedef alır. PCI-DSS ve birçok kurumsal güvenlik standardı bunları yasaklamış durumda.
Windows Registry üzerinden TLS protokollerini yönetmek en güvenilir yöntem:
# TLS 1.0'ı devre dışı bırak (Sunucu ve İstemci olarak)
$tls10Path = "HKLM:SYSTEMCurrentControlSetControlSecurityProvidersSCHANNELProtocolsTLS 1.0"
New-Item -Path "$tls10PathServer" -Force
New-Item -Path "$tls10PathClient" -Force
Set-ItemProperty -Path "$tls10PathServer" -Name "Enabled" -Value 0 -Type DWord
Set-ItemProperty -Path "$tls10PathServer" -Name "DisabledByDefault" -Value 1 -Type DWord
Set-ItemProperty -Path "$tls10PathClient" -Name "Enabled" -Value 0 -Type DWord
Set-ItemProperty -Path "$tls10PathClient" -Name "DisabledByDefault" -Value 1 -Type DWord
# TLS 1.1'i devre dışı bırak
$tls11Path = "HKLM:SYSTEMCurrentControlSetControlSecurityProvidersSCHANNELProtocolsTLS 1.1"
New-Item -Path "$tls11PathServer" -Force
New-Item -Path "$tls11PathClient" -Force
Set-ItemProperty -Path "$tls11PathServer" -Name "Enabled" -Value 0 -Type DWord
Set-ItemProperty -Path "$tls11PathServer" -Name "DisabledByDefault" -Value 1 -Type DWord
Set-ItemProperty -Path "$tls11PathClient" -Name "Enabled" -Value 0 -Type DWord
Set-ItemProperty -Path "$tls11PathClient" -Name "DisabledByDefault" -Value 1 -Type DWord
# TLS 1.2'yi açık tut (zaten açık olmalı ama emin olalım)
$tls12Path = "HKLM:SYSTEMCurrentControlSetControlSecurityProvidersSCHANNELProtocolsTLS 1.2"
New-Item -Path "$tls12PathServer" -Force
Set-ItemProperty -Path "$tls12PathServer" -Name "Enabled" -Value 1 -Type DWord
Set-ItemProperty -Path "$tls12PathServer" -Name "DisabledByDefault" -Value 0 -Type DWord
# TLS 1.3'ü etkinleştir (Windows Server 2022 ve üstü destekler)
$tls13Path = "HKLM:SYSTEMCurrentControlSetControlSecurityProvidersSCHANNELProtocolsTLS 1.3"
New-Item -Path "$tls13PathServer" -Force
Set-ItemProperty -Path "$tls13PathServer" -Name "Enabled" -Value 1 -Type DWord
Set-ItemProperty -Path "$tls13PathServer" -Name "DisabledByDefault" -Value 0 -Type DWord
Bu değişiklikler sonrası sunucuyu yeniden başlatmanız gerekiyor. Üretim ortamında bunu bakım penceresi sırasında yapın. Registry değişikliklerini otomatikleştirmek için IISCrypto aracını da kullanabilirsiniz, GUI ile birkaç tıkla aynı işlemi yapar.
Güvenli Cipher Suite Sıralaması
TLS protokolünü doğru ayarlasanız bile zayıf şifre takımlarını açık bırakmak risk oluşturur. RC4, DES, 3DES gibi algoritmalar kesinlikle kapatılmalı:
# RC4'ü devre dışı bırak
$ciphersPath = "HKLM:SYSTEMCurrentControlSetControlSecurityProvidersSCHANNELCiphers"
New-Item -Path "$ciphersPathRC4 128/128" -Force
Set-ItemProperty -Path "$ciphersPathRC4 128/128" -Name "Enabled" -Value 0 -Type DWord
New-Item -Path "$ciphersPathRC4 64/128" -Force
Set-ItemProperty -Path "$ciphersPathRC4 64/128" -Name "Enabled" -Value 0 -Type DWord
New-Item -Path "$ciphersPathRC4 56/128" -Force
Set-ItemProperty -Path "$ciphersPathRC4 56/128" -Name "Enabled" -Value 0 -Type DWord
# DES ve 3DES'i devre dışı bırak
New-Item -Path "$ciphersPathDES 56/56" -Force
Set-ItemProperty -Path "$ciphersPathDES 56/56" -Name "Enabled" -Value 0 -Type DWord
New-Item -Path "$ciphersPathTriple DES 168" -Force
Set-ItemProperty -Path "$ciphersPathTriple DES 168" -Name "Enabled" -Value 0 -Type DWord
Güvenlik Header’larını Yapılandırmak
HTTP güvenlik header’ları, modern tarayıcıların güvenlik özelliklerini etkinleştiren mekanizmalardır. XSS, clickjacking ve content sniffing saldırılarına karşı önemli bir katman oluştururlar.
<!-- web.config veya applicationHost.config içine eklenecek -->
<system.webServer>
<httpProtocol>
<customHeaders>
<!-- Clickjacking koruması -->
<add name="X-Frame-Options" value="SAMEORIGIN" />
<!-- Content type sniffing koruması -->
<add name="X-Content-Type-Options" value="nosniff" />
<!-- XSS filtresi (eski tarayıcılar için) -->
<add name="X-XSS-Protection" value="1; mode=block" />
<!-- HSTS: Tarayıcıyı her zaman HTTPS kullanmaya zorla -->
<add name="Strict-Transport-Security" value="max-age=31536000; includeSubDomains; preload" />
<!-- Referrer bilgisini kısıtla -->
<add name="Referrer-Policy" value="strict-origin-when-cross-origin" />
<!-- Permissions Policy -->
<add name="Permissions-Policy" value="geolocation=(), microphone=(), camera=()" />
</customHeaders>
</httpProtocol>
</system.webServer>
Strict-Transport-Security header’ını eklemeden önce dikkatli olun. max-age=31536000 değeri tarayıcıya “bu siteye bir yıl boyunca sadece HTTPS ile bağlan” demek. Sitenizde HTTP’ye ihtiyaç duyarsanız bu header sorun çıkarır. Önce max-age=3600 ile test edin.
Request Filtering ile Saldırı Yüzeyini Azaltmak
IIS’in built-in request filtering modülü, kötü niyetli istekleri uygulama katmanına ulaşmadan önce bloklayabilir.
# Maksimum URL uzunluğunu sınırla (varsayılan 4096, 2048 genellikle yeterli)
Set-WebConfigurationProperty -pspath 'MACHINE/WEBROOT/APPHOST' `
-filter "system.webServer/security/requestFiltering/requestLimits" `
-name "maxUrl" -value 2048
# Maksimum query string uzunluğunu sınırla
Set-WebConfigurationProperty -pspath 'MACHINE/WEBROOT/APPHOST' `
-filter "system.webServer/security/requestFiltering/requestLimits" `
-name "maxQueryString" -value 2048
# Maksimum content length (örnek: 30MB)
Set-WebConfigurationProperty -pspath 'MACHINE/WEBROOT/APPHOST' `
-filter "system.webServer/security/requestFiltering/requestLimits" `
-name "maxAllowedContentLength" -value 31457280
# Double encoding saldırılarını engelle
Set-WebConfigurationProperty -pspath 'MACHINE/WEBROOT/APPHOST' `
-filter "system.webServer/security/requestFiltering" `
-name "allowDoubleEscaping" -value "false"
# High bit karakterleri engelle
Set-WebConfigurationProperty -pspath 'MACHINE/WEBROOT/APPHOST' `
-filter "system.webServer/security/requestFiltering" `
-name "allowHighBitCharacters" -value "false"
Şüpheli dosya uzantılarını da engelleyebilirsiniz:
# .exe uzantısına erişimi engelle
Add-WebConfigurationProperty -pspath 'MACHINE/WEBROOT/APPHOST' `
-filter "system.webServer/security/requestFiltering/fileExtensions" `
-name "." -value @{fileExtension='.exe'; allowed='false'}
# .bat dosyalarını engelle
Add-WebConfigurationProperty -pspath 'MACHINE/WEBROOT/APPHOST' `
-filter "system.webServer/security/requestFiltering/fileExtensions" `
-name "." -value @{fileExtension='.bat'; allowed='false'}
# .config dosyalarına dışarıdan erişimi engelle
Add-WebConfigurationProperty -pspath 'MACHINE/WEBROOT/APPHOST' `
-filter "system.webServer/security/requestFiltering/fileExtensions" `
-name "." -value @{fileExtension='.config'; allowed='false'}
Uygulama Havuzu Kimliği ve İzolasyonu
Bu kısım çoğunlukla görmezden geliniyor ama son derece önemli. Varsayılan olarak uygulama havuzları ApplicationPoolIdentity hesabıyla çalışır ki bu iyi bir başlangıç noktasıdır. Ama çok sayıda uygulama aynı sunucuda çalışıyorsa mutlaka izole edilmeli.
# Her uygulama için ayrı uygulama havuzu oluştur
New-WebAppPool -Name "MyApp_Pool"
# .NET CLR versiyonunu belirle
Set-ItemProperty -Path "IIS:AppPoolsMyApp_Pool" `
-Name managedRuntimeVersion -Value "v4.0"
# Uygulama havuzunun 32-bit modda çalışıp çalışmayacağını belirle
Set-ItemProperty -Path "IIS:AppPoolsMyApp_Pool" `
-Name enable32BitAppOnWin64 -Value $false
# Maksimum işçi süreci sayısını belirle (Web Garden - güvenlik açısından 1 bırakın)
Set-ItemProperty -Path "IIS:AppPoolsMyApp_Pool" `
-Name processModel.maxProcesses -Value 1
# Uygulama havuzunun otomatik yeniden başlamasını yapılandır
Set-ItemProperty -Path "IIS:AppPoolsMyApp_Pool" `
-Name recycling.periodicRestart.time -Value "02:00:00"
# Boşta kalma zaten otomatik kapatma süresini ayarla
Set-ItemProperty -Path "IIS:AppPoolsMyApp_Pool" `
-Name processModel.idleTimeout -Value "00:20:00"
Uygulama havuzunu özel bir servis hesabıyla çalıştırmanız gerekiyorsa:
# Servis hesabı kullan (domain ortamları için)
Set-ItemProperty -Path "IIS:AppPoolsMyApp_Pool" `
-Name processModel.userName -Value "DOMAINsvc_myapp"
Set-ItemProperty -Path "IIS:AppPoolsMyApp_Pool" `
-Name processModel.password -Value "GucluBirSifre123!"
Set-ItemProperty -Path "IIS:AppPoolsMyApp_Pool" `
-Name processModel.identityType -Value 3
Bu servis hesabına minimum gerekli izinleri verin. Web kök dizinine Read ve Execute yeterlidir, Write ve Modify çoğu durumda gerekmez.
Directory Browsing ve Hata Sayfaları
Directory browsing kapalı olmalı. Bunu özellikle belirtme nedenin şu: birden fazla site barındıran sunucularda yeni site eklendiğinde default olarak açık geliyor bazı IIS versiyonlarında.
# Tüm sunucu genelinde directory browsing'i kapat
Set-WebConfigurationProperty -pspath 'MACHINE/WEBROOT/APPHOST' `
-filter "system.webServer/directoryBrowse" `
-name "enabled" -value "false"
# Belirli bir site için de ayrıca kapat
Set-WebConfigurationProperty -pspath 'MACHINE/WEBROOT/APPHOST/Default Web Site' `
-filter "system.webServer/directoryBrowse" `
-name "enabled" -value "false"
Özel hata sayfaları yapılandırarak stack trace ve sistem bilgisi ifşasını önleyin:
<system.webServer>
<httpErrors errorMode="Custom" existingResponse="Replace">
<remove statusCode="400" />
<remove statusCode="401" />
<remove statusCode="403" />
<remove statusCode="404" />
<remove statusCode="500" />
<error statusCode="400" path="/hata/400.html" responseMode="File" />
<error statusCode="401" path="/hata/401.html" responseMode="File" />
<error statusCode="403" path="/hata/403.html" responseMode="File" />
<error statusCode="404" path="/hata/404.html" responseMode="File" />
<error statusCode="500" path="/hata/500.html" responseMode="File" />
</httpErrors>
</system.webServer>
errorMode="Custom" hem üretim hem test ortamlarında kullanılır. Geliştirme ortamında errorMode="Detailed" kullanabilirsiniz ama bu ayar kesinlikle üretim sunucusuna çıkmamalı.
Gerçek Dünya Senaryosu: Sertleştirme Sonrası Doğrulama
Bütün bu değişiklikleri yaptıktan sonra nasıl test edersiniz? İşte kullandığım yöntemler:
SSL Labs testi için https://www.ssllabs.com/ssltest/ adresine sitenizi girin. A veya A+ notu almanız gerekiyor. TLS yapılandırmasını düzgün yaptıysanız B ile kalmayın.
Güvenlik header’larını kontrol etmek için https://securityheaders.com adresini kullanabilirsiniz. Missing header’ları tek tek gösterir.
Lokal test için PowerShell ile basit bir HTTP isteği gönderin:
# Response header'larını kontrol et
$response = Invoke-WebRequest -Uri "https://siteniz.com" -Method GET
$response.Headers | Format-Table -AutoSize
# TLS versiyonunu ve sertifika bilgisini kontrol et
$tcpClient = New-Object Net.Sockets.TcpClient("siteniz.com", 443)
$sslStream = New-Object Net.Security.SslStream($tcpClient.GetStream(), $false, {$true})
$sslStream.AuthenticateAsClient("siteniz.com")
Write-Host "TLS Versiyonu: $($sslStream.SslProtocol)"
Write-Host "Cipher: $($sslStream.CipherAlgorithm)"
$sslStream.Close()
$tcpClient.Close()
Bu scripti haftada bir çalıştırın. Özellikle Windows Update sonrasında TLS yapılandırması bazen değişebiliyor, takip etmek önemli.
Loglama ve İzleme
Sertleştirme bir kez yapılıp unutulan bir iş değil. Logları düzenli izleyin:
# IIS loglarının nerede tutulduğunu bul
Get-WebConfiguration -pspath 'MACHINE/WEBROOT/APPHOST' `
-filter "system.applicationHost/sites/site[@name='Default Web Site']/logFile" |
Select-Object -Property directory, period, logFormat
# Failed Request Tracing'i etkinleştir (sorun tespiti için)
Enable-WebRequestTracing -Name "Default Web Site"
# 400 ve 500 hatalarını izlemek için Failed Request Tracing kuralı ekle
Add-WebConfigurationProperty -pspath 'MACHINE/WEBROOT/APPHOST/Default Web Site' `
-filter "system.webServer/tracing/traceFailedRequests" `
-name "." -value @{path='*'}
IIS loglarını merkezi bir SIEM sistemine göndermek için Windows Event Forwarding veya Elastic Agent kullanabilirsiniz. 400 hata oranında ani artış genellikle tarama veya brute force girişiminin işareti.
Sonuç
IIS güvenlik sertleştirmesi, birbirinden bağımsız küçük ayarların bir araya gelmesiyle oluşan katmanlı bir süreç. Versiyon bilgisini gizlemek, TLS yapılandırmasını sıkılaştırmak, güvenlik header’larını eklemek, request filtering’i yapılandırmak ve uygulama havuzlarını izole etmek. Bunların hiçbiri başlı başına yeterli değil ama hepsi bir arada ciddi bir savunma katmanı oluşturuyor.
Şunu da söyleyelim: bu yazıda bahsedilen ayarları uygulamadan önce test ortamında deneyin. Özellikle TLS değişiklikleri ve request filtering kuralları, bazı uygulamalarda beklenmedik sorunlara yol açabilir. Değişiklikleri kayıt altına alın, geri alma planınız olsun.
En sık gördüğüm hata şu: sertleştirme yapılıyor, denetim geçiliyor, sonra bir yıl sonra yeni bir uygulama eklendiğinde eski alışkanlıklara dönülüyor. Bu yüzden bu ayarları bir Ansible playbook veya PowerShell Desired State Configuration (DSC) scriptiyle otomatize edin. Her yeni sunucu ve uygulama dağıtımında standart sertleştirme adımlarının otomatik uygulandığından emin olun. Güvenlik bir proje değil, süreçtir.
