IIS'te Güvenlik Sertleştirme Rehberi — .NET Uygulama Sunucunuzu Kilitleyin
IIS'te Güvenlik Sertleştirme Rehberi — .NET Uygulama Sunucunuzu Kilitleyin
Neden IIS Sertleştirme Çoğu Zaman Göz Ardı Edilir?
Çoğu .NET geliştiricisi uygulama seviyesi güvenliğe odaklanır — input validation, authentication, authorization. Ancak uygulamanızın önünde duran web sunucusu eşit derecede önemli bir saldırı yüzeyidir. Varsayılan IIS kurulumu işlevseldir, güvenli değildir. Her saldırgana kendi sürümünü duyurur, tehlikeli HTTP metodlarını kabul eder ve onlarca güvenlik header'ını yapılandırılmamış bırakır.
BDDK güvenlik denetimlerinden geçtim. Bulgular listesi hiçbir zaman kısa olmaz. Bu rehber gerçekten işaretlenen konuları ve nasıl düzeltileceğini ele alıyor.
1. Sunucu Versiyon Bilgisini Gizleyin
Varsayılan olarak IIS, her HTTP yanıtında kendini açıkça tanıtır:
Server: Microsoft-IIS/10.0
X-Powered-By: ASP.NET
X-AspNet-Version: 4.0.30319
Bu, saldırganlar için bedava istihbarat demektir. Hepsini kaldırın.
web.config ile X-Powered-By ve X-AspNet-Version kaldırma:
<configuration>
<system.web>
<httpRuntime enableVersionHeader="false" />
</system.web>
<system.webServer>
<httpProtocol>
<customHeaders>
<remove name="X-Powered-By" />
</customHeaders>
</httpProtocol>
</system.webServer>
</configuration>
Server header'ını URL Rewrite ile kaldırma:
<rewrite>
<outboundRules>
<rule name="Remove Server Header">
<match serverVariable="RESPONSE_SERVER" pattern=".+" />
<action type="Rewrite" value="" />
</rule>
</outboundRules>
</rewrite>
Doğrulama:
Invoke-WebRequest -Uri "https://yourdomain.com" -Method HEAD |
Select-Object -ExpandProperty Headers
Server header'ı artık boş veya hiç görünmüyor olmalıdır.
2. Güvenlik Header'ları — Vazgeçilmez Liste
Güvenlik header'ları, en az çabayla en yüksek etkiyi sağlayan sertleştirme adımıdır. Tamamını web.config'e ekleyin:
<system.webServer>
<httpProtocol>
<customHeaders>
<!-- Clickjacking engellemesi -->
<add name="X-Frame-Options" value="SAMEORIGIN" />
<!-- MIME type tahminini durdurma -->
<add name="X-Content-Type-Options" value="nosniff" />
<!-- Eski tarayıcılarda XSS filtresi -->
<add name="X-XSS-Protection" value="1; mode=block" />
<!-- 1 yıl boyunca HTTPS zorunluluğu -->
<add name="Strict-Transport-Security"
value="max-age=31536000; includeSubDomains; preload" />
<!-- Referrer bilgisini kontrol etme -->
<add name="Referrer-Policy" value="strict-origin-when-cross-origin" />
<!-- Tarayıcı özelliklerini kısıtlama -->
<add name="Permissions-Policy"
value="geolocation=(), microphone=(), camera=(), payment=()" />
<!-- İçerik Güvenlik Politikası — uygulamanıza göre ayarlayın -->
<add name="Content-Security-Policy"
value="default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data:; font-src 'self'; frame-ancestors 'none';" />
<!-- X-Powered-By kaldırma -->
<remove name="X-Powered-By" />
</customHeaders>
</httpProtocol>
</system.webServer>
Header bazında açıklama:
| Header | Amaç | Önerilen Değer |
|---|---|---|
| X-Frame-Options | Sitenizin iframe içine gömülmesini engeller (clickjacking) | SAMEORIGIN |
| X-Content-Type-Options | Tarayıcıların içerik türü tahminini durdurur | nosniff |
| X-XSS-Protection | Eski IE/Chrome için XSS filtresi | 1; mode=block |
| Strict-Transport-Security | HTTPS'i zorunlu kılar, HTTP kullanımını engeller | max-age=31536000; includeSubDomains |
| Referrer-Policy | Referer header'ında hangi URL'nin gönderileceğini kontrol eder | strict-origin-when-cross-origin |
| Permissions-Policy | Uygulamanızın kullanmadığı tarayıcı özelliklerini devre dışı bırakır | Kullanılmayan her şeyi kısıtlayın |
| Content-Security-Policy | İzin verilen içerik kaynaklarını tanımlar — XSS'i önler | Aşamalı olarak sıkılaştırın |
CSP notu: CSP güçlüdür ancak dikkatli ayarlama gerektirir. Zorlamadan önce ihlalleri gözlemlemek için Report-Only moduyla başlayın:
<add name="Content-Security-Policy-Report-Only"
value="default-src 'self'; report-uri /csp-report" />
3. TLS Yapılandırması — Zayıf Protokol ve Cipher'ları Devre Dışı Bırakın
Her güvenlik denetiminde işaretlenen konu budur. Varsayılan olarak Windows Server hâlâ TLS 1.0, TLS 1.1 ve zayıf cipher suite'leri destekliyor olabilir.
Mevcut TLS desteğini kontrol edin:
$url = "https://yourdomain.com"
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
Invoke-WebRequest $url
Registry üzerinden TLS 1.0 ve 1.1'i devre dışı bırakın:
# TLS 1.0'ı devre dışı bırak
$tls10 = "HKLM:\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols\TLS 1.0\Server"
New-Item -Path $tls10 -Force
Set-ItemProperty -Path $tls10 -Name "Enabled" -Value 0 -Type DWord
Set-ItemProperty -Path $tls10 -Name "DisabledByDefault" -Value 1 -Type DWord
# TLS 1.1'i devre dışı bırak
$tls11 = "HKLM:\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols\TLS 1.1\Server"
New-Item -Path $tls11 -Force
Set-ItemProperty -Path $tls11 -Name "Enabled" -Value 0 -Type DWord
Set-ItemProperty -Path $tls11 -Name "DisabledByDefault" -Value 1 -Type DWord
# TLS 1.2'yi etkinleştir
$tls12 = "HKLM:\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols\TLS 1.2\Server"
New-Item -Path $tls12 -Force
Set-ItemProperty -Path $tls12 -Name "Enabled" -Value 1 -Type DWord
Set-ItemProperty -Path $tls12 -Name "DisabledByDefault" -Value 0 -Type DWord
# TLS 1.3'ü etkinleştir (Windows Server 2022+)
$tls13 = "HKLM:\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols\TLS 1.3\Server"
New-Item -Path $tls13 -Force
Set-ItemProperty -Path $tls13 -Name "Enabled" -Value 1 -Type DWord
Set-ItemProperty -Path $tls13 -Name "DisabledByDefault" -Value 0 -Type DWord
Zayıf cipher suite'leri devre dışı bırakın:
# RC4'ü devre dışı bırak
$rc4 = "HKLM:\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Ciphers\RC4 128/128"
New-Item -Path $rc4 -Force
Set-ItemProperty -Path $rc4 -Name "Enabled" -Value 0 -Type DWord
# 3DES'i devre dışı bırak
$3des = "HKLM:\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Ciphers\Triple DES 168"
New-Item -Path $3des -Force
Set-ItemProperty -Path $3des -Name "Enabled" -Value 0 -Type DWord
# NULL cipher'ı devre dışı bırak
$null = "HKLM:\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Ciphers\NULL"
New-Item -Path $null -Force
Set-ItemProperty -Path $null -Name "Enabled" -Value 0 -Type DWord
IIS Crypto ile kolay yönetim:
Nartac Software'in IIS Crypto aracı, birkaç tıklamayla en iyi uygulama şablonlarını uygulamanızı sağlar. "Best Practices" şablonunu uygulayıp yeniden başlatın. Registry key'lerini elle düzenlemekten çok daha güvenlidir.
SSL Labs ile doğrulayın:
Değişikliklerden sonra https://www.ssllabs.com/ssltest/ adresinde test edin — A veya A+ derecelendirmesi hedefleyin.
4. Gereksiz HTTP Metodlarını Devre Dışı Bırakın
Varsayılan olarak IIS, uygulamanızın hiç kullanmadığı HTTP metodlarını kabul eder. PUT, DELETE, TRACE ve OPTIONS metodları saldırı vektörleri olabilir.
Request Filtering ile izin verilen metodları kısıtlayın:
<system.webServer>
<security>
<requestFiltering>
<verbs allowUnlisted="false">
<add verb="GET" allowed="true" />
<add verb="POST" allowed="true" />
<add verb="HEAD" allowed="true" />
<!-- Yalnızca uygulamanızın gerçekten ihtiyaç duyduklarını ekleyin -->
</verbs>
</requestFiltering>
</security>
</system.webServer>
TRACE metodunu devre dışı bırakın — Cross-Site Tracing (XST) saldırılarını engeller:
# IIS Manager üzerinden veya appcmd ile
%systemroot%\system32\inetsrv\appcmd set config /section:requestfiltering /+verbs.[verb='TRACE',allowed='false']
Doğrulama:
Invoke-WebRequest -Uri "https://yourdomain.com" -Method TRACE
# 405 Method Not Allowed döndürmeli
5. Request Filtering — Kötü Niyetli İstekleri Engelleyin
IIS'in Request Filtering modülü, uygulama kodunuza ulaşmadan önce kötü amaçlı istekleri engeller.
<system.webServer>
<security>
<requestFiltering>
<!-- Maksimum URL uzunluğu (varsayılan 4096) -->
<requestLimits maxUrl="2048"
maxQueryString="1024"
maxAllowedContentLength="10485760" />
<!-- Çift encode edilmiş istekleri reddet -->
<requestFiltering allowDoubleEscaping="false" />
<!-- Yüksek riskli uzantıları engelle -->
<fileExtensions allowUnlisted="true">
<add fileExtension=".config" allowed="false" />
<add fileExtension=".bak" allowed="false" />
<add fileExtension=".log" allowed="false" />
<add fileExtension=".mdf" allowed="false" />
<add fileExtension=".ldf" allowed="false" />
<add fileExtension=".sql" allowed="false" />
</fileExtensions>
<!-- Gizli segmentleri engelle -->
<hiddenSegments>
<add segment="web.config" />
<add segment="appsettings.json" />
<add segment=".git" />
<add segment="bin" />
<add segment="obj" />
</hiddenSegments>
</requestFiltering>
</security>
</system.webServer>
6. Hata Sayfaları — Detayları Gizleyin
Varsayılan .NET hata sayfaları stack trace, dosya yolu ve versiyon bilgisi içerir. Bunların tamamı saldırgan için değerli veridir.
web.config'de özel hata sayfaları:
<configuration>
<system.web>
<!-- Detaylı hataları yalnızca local'de göster -->
<customErrors mode="RemoteOnly" defaultRedirect="/error/500">
<error statusCode="404" redirect="/error/404" />
<error statusCode="403" redirect="/error/403" />
<error statusCode="500" redirect="/error/500" />
</customErrors>
</system.web>
<system.webServer>
<httpErrors errorMode="Custom" existingResponse="Replace">
<remove statusCode="404" />
<remove statusCode="500" />
<error statusCode="404" path="/error/404" responseMode="ExecuteURL" />
<error statusCode="500" path="/error/500" responseMode="ExecuteURL" />
</httpErrors>
</system.webServer>
</configuration>
Program.cs'de exception detaylarını gizleyin (.NET 8):
if (app.Environment.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler("/error/500");
app.UseHsts();
}
7. Application Pool Güvenliği
Application Pool kimliği, IIS süreçlerinin hangi Windows hesabıyla çalıştığını belirler. Asla LocalSystem veya Administrator kullanmayın.
En az ayrıcalık prensibi:
# Application Pool'u ApplicationPoolIdentity ile yapılandır
# (IIS Manager > Application Pools > Advanced Settings)
# PowerShell ile:
Import-Module WebAdministration
Set-ItemProperty IIS:\AppPools\YourAppPool -Name processModel.userName -Value "ApplicationPoolIdentity"
Set-ItemProperty IIS:\AppPools\YourAppPool -Name processModel.password -Value ""
Set-ItemProperty IIS:\AppPools\YourAppPool -Name processModel.identityType -Value 4
32-bit uygulamayı 64-bit pool'da çalıştırmayın:
Set-ItemProperty IIS:\AppPools\YourAppPool -Name enable32BitAppOnWin64 -Value false
Idle timeout ve rapid fail protection:
# 20 dakika idle sonra pool'u durdur
Set-ItemProperty IIS:\AppPools\YourAppPool -Name processModel.idleTimeout -Value "00:20:00"
# 5 dakikada 5'ten fazla crash olursa pool'u durdur
Set-ItemProperty IIS:\AppPools\YourAppPool -Name failure.rapidFailProtection -Value true
Set-ItemProperty IIS:\AppPools\YourAppPool -Name failure.rapidFailProtectionInterval -Value "00:05:00"
Set-ItemProperty IIS:\AppPools\YourAppPool -Name failure.rapidFailProtectionMaxCrashes -Value 5
8. HTTPS Yönlendirmesi ve HSTS
HTTP üzerinden gelen her isteği HTTPS'e yönlendirin ve HSTS ile tarayıcıları bir daha HTTP kullanmamaya zorlayın.
web.config ile HTTP → HTTPS yönlendirme:
<rewrite>
<rules>
<rule name="HTTP to HTTPS" stopProcessing="true">
<match url="(.*)" />
<conditions>
<add input="{HTTPS}" pattern="^OFF$" />
</conditions>
<action type="Redirect"
url="https://{HTTP_HOST}/{R:1}"
redirectType="Permanent" />
</rule>
</rules>
</rewrite>
Program.cs'de HSTS (.NET 8):
builder.Services.AddHsts(options =>
{
options.Preload = true;
options.IncludeSubDomains = true;
options.MaxAge = TimeSpan.FromDays(365);
});
app.UseHsts();
app.UseHttpsRedirection();
9. Dosya ve Dizin Listeleme
Dizin listeleme etkinse, saldırganlar dosya yapınızı görebilir. Kapalı olduğundan emin olun.
<system.webServer>
<directoryBrowse enabled="false" />
</system.webServer>
PowerShell ile tüm site'larda kontrol:
Get-WebConfigurationProperty -Filter system.webServer/directoryBrowse `
-PSPath IIS:\ -Recurse -Name enabled |
Where-Object { $_.Value -eq $true }
Herhangi bir sonuç dönüyorsa o site'larda dizin listeleme hâlâ açıktır.
10. Denetim Listesi — Üretime Almadan Önce
Tüm adımları uyguladıktan sonra şu listeyi kontrol edin:
✅ Server / X-Powered-By / X-AspNet-Version header'ları kaldırıldı
✅ X-Frame-Options, X-Content-Type-Options, HSTS header'ları eklendi
✅ Content-Security-Policy yapılandırıldı
✅ TLS 1.0 ve TLS 1.1 devre dışı bırakıldı
✅ RC4, 3DES, NULL cipher'lar devre dışı bırakıldı
✅ SSL Labs testi A veya A+ sonucu verdi
✅ TRACE, PUT, DELETE metodları kısıtlandı (ihtiyaç yoksa)
✅ .config, .bak, .log uzantıları Request Filtering ile engellendi
✅ Özel hata sayfaları yapılandırıldı, stack trace gizlendi
✅ Application Pool ApplicationPoolIdentity kullanıyor
✅ HTTP → HTTPS yönlendirmesi aktif
✅ Dizin listeleme kapalı
✅ web.config ve appsettings.json hidden segment olarak tanımlandı
Temel Çıkarımlar
- Varsayılan IIS kurulumu işlevseldir, güvenli değildir — her üretim sunucusu sertleştirilmelidir
- Versiyon bilgisini açıklamak saldırganlara bedava istihbarat vermektir
- Güvenlik header'ları dakikalar içinde uygulanabilir, etkisi yüksektir
- TLS 1.0/1.1 ve zayıf cipher'lar her denetimde işaretlenir — önce bunları kapatın
- Application Pool'u her zaman en az ayrıcalıklı kimlikle çalıştırın
- SSL Labs A+ puanı bir lüks değil, minimum standarttır
Güvenlik, sonradan eklenen bir özellik değildir. Sunucunuzu yapılandırdığınız anda başlar — ilk isteği aldığınızda değil.