IIS’te Request Filtering ile Güvenlik Sertleştirme
Production ortamında IIS çalıştıran herkes bir gün mutlaka şu soruyla yüzleşir: “Bu sunucu ne kadar güvenli?” Güvenlik duvarı var, SSL sertifikası var, belki bir WAF bile kurdunuz. Ama IIS’in kendi içinde, istek düzeyinde yapabileceğiniz sertleştirme adımlarını atladıysanız, kapıyı açık bırakmış sayılırsınız. Request Filtering tam da bu boşluğu dolduruyor.
Request Filtering Nedir ve Neden Önemlidir
IIS’te Request Filtering, gelen HTTP isteklerini sunucunuza ulaşmadan önce filtrelemek için kullanılan yerleşik bir modüldür. mod_security gibi üçüncü parti çözümlere benzer bir işlev görür ama IIS’e özgü, kernel düzeyine yakın çalışan, performans açısından çok daha verimli bir yapıdır.
Temel mantığı şudur: Kötü niyetli istekler uygulamanıza ulaşmadan 404 veya 400 hatası ile reddedilir. Bu sayede uygulama katmanındaki zafiyetler bile teorik olarak daha az tehdit oluşturur çünkü istek zaten IIS seviyesinde düşürülmüştür.
Özellikle şu saldırı vektörlerine karşı etkilidir:
- Dizin geçişi saldırıları (
../../../windows/system32gibi) - Çift URL kodlama (
%252e%252egibi obfuscation teknikleri) - Anormal uzunluktaki istekler (buffer overflow girişimleri)
- Yasaklı HTTP metodları (PUT, DELETE, TRACE vs.)
- Hassas dosya uzantılarına erişim (.config, .bak, .log dosyaları)
IIS’te Request Filtering Modülünü Etkinleştirme
Windows Server 2012 R2 ve sonrası için modül zaten kurulu gelir ama yine de doğrulamak gerekir.
# Request Filtering modülünün kurulu olup olmadığını kontrol et
Get-WindowsFeature Web-Filtering
# Kurulu değilse yükle
Install-WindowsFeature Web-Filtering
IIS Manager üzerinden de kontrol edebilirsiniz: Site veya sunucu düzeyinde “Request Filtering” ikonu görünüyorsa modül aktiftir.
Konfigürasyon dosyası olarak web.config veya applicationHost.config üzerinde çalışacaksınız. Büyük ortamlarda her şeyi applicationHost.config üzerinden merkezi yönetmek çok daha mantıklı, site bazlı istisnalar için ise web.config devreye girer.
Temel Konfigürasyon: web.config Üzerinden
En basit haliyle bir Request Filtering bloğu şöyle görünür:
<system.webServer>
<security>
<requestFiltering>
<requestLimits
maxAllowedContentLength="30000000"
maxUrl="2048"
maxQueryString="1024" />
<verbs allowUnlisted="false">
<add verb="GET" allowed="true" />
<add verb="POST" allowed="true" />
<add verb="HEAD" allowed="true" />
</verbs>
<fileExtensions allowUnlisted="true">
<add fileExtension=".config" allowed="false" />
<add fileExtension=".bak" allowed="false" />
<add fileExtension=".log" allowed="false" />
<add fileExtension=".sql" allowed="false" />
</fileExtensions>
<hiddenSegments>
<add segment="bin" />
<add segment="App_Code" />
<add segment="App_Data" />
</hiddenSegments>
</requestFiltering>
</security>
</system.webServer>
Bu konfigürasyon birkaç şeyi aynı anda yapıyor: İçerik boyutunu sınırlıyor, URL uzunluğunu kısıtlıyor, sadece belirli HTTP metodlarına izin veriyor, hassas dosya uzantılarını engelliyor ve önemli dizinleri gizliyor.
HTTP Metodlarını Kısıtlamak
Üretim ortamında gördüğüm en yaygın hataların başında gereksiz HTTP metodlarına izin vermek geliyor. TRACE metodu başlı başına bir güvenlik riskidir; XST (Cross-Site Tracing) saldırılarında kullanılır. OPTIONS metodu saldırgana sunucunuzun hangi metodları desteklediğini söyler. DELETE ve PUT ise yanlış yapılandırılmış uygulamalarda doğrudan dosya silme ve yazma işlemlerine kapı aralayabilir.
# PowerShell ile tüm siteye HTTP metod kısıtlaması uygula
# Önce mevcut izinleri temizle
$siteName = "YourSiteName"
# Sadece ihtiyaç duyulan metodlara izin ver
Add-WebConfigurationProperty -pspath "MACHINE/WEBROOT/APPHOST/$siteName" `
-filter "system.webServer/security/requestFiltering/verbs" `
-name "." `
-value @{verb='GET'; allowed='true'}
Add-WebConfigurationProperty -pspath "MACHINE/WEBROOT/APPHOST/$siteName" `
-filter "system.webServer/security/requestFiltering/verbs" `
-name "." `
-value @{verb='POST'; allowed='true'}
# allowUnlisted parametresini false yap (listelenmeyen metodları engelle)
Set-WebConfigurationProperty -pspath "MACHINE/WEBROOT/APPHOST/$siteName" `
-filter "system.webServer/security/requestFiltering/verbs" `
-name "allowUnlisted" `
-value "false"
Bir REST API yönetiyorsanız PUT ve DELETE de gerekli olacaktır. Ama kurumsal web sitelerinin büyük çoğunluğu için GET, POST ve HEAD yeterlidir.
URL ve Query String Uzunluk Sınırları
SQL injection ve XSS saldırılarının büyük kısmı anormal uzunluktaki query string’ler içerir. Saldırgan bir payload gönderiyor ve uygulamanızın nasıl tepki verdiğini gözlemliyorsa, bunu zaten URL uzunluğundan anlayabilirsiniz.
<requestLimits
maxAllowedContentLength="10485760"
maxUrl="4096"
maxQueryString="2048"
maxQueryStringLength="2048"
headerLimits>
<add header="Content-type" sizeLimit="100" />
<add header="User-Agent" sizeLimit="300" />
</requestLimits>
maxAllowedContentLength değeri byte cinsindendir. 10485760 yaklaşık 10 MB’a karşılık gelir. Dosya yükleme kabul eden bir uygulama çalıştırıyorsanız bu değeri ihtiyaca göre artırmanız gerekebilir ama mümkün olduğunca düşük tutun.
maxUrl için 2048 ya da en fazla 4096 makul bir değerdir. Gerçek dünyada meşru bir kullanıcının 4096 karakterden uzun bir URL girmesi neredeyse imkânsızdır.
Dizin Geçişi Saldırılarını Engelleme
Request Filtering, . ve .. içeren URL segmentlerini varsayılan olarak engeller. Ama bunu manuel olarak da doğrulamak ve güçlendirmek gerekir.
<requestFiltering allowDoubleEscaping="false">
<denyStrings>
<add string="../" />
<add string=".."/>
<add string="/./" />
<add string="//"/>
</denyStrings>
</requestFiltering>
allowDoubleEscaping="false" özellikle kritik. Çift URL kodlama, IIS’in eski sürümlerinde dizin geçişine izin verebiliyordu. Örneğin %2e%2e%2f normal bir ../ anlamına gelir ama çift kodlandığında %252e%252e%252f görünümünü alır ve bazı sistemler bunu decode etmeden geçirebilir.
Hassas Dosya Uzantılarını Engelleme
Gerçek bir penetrasyon testinde ilk yaptığımız şeylerden biri .bak, .old, .config gibi uzantılara sahip dosyaları taramaktır. Şaşırtıcı biçimde pek çok sunucuda bu dosyalar erişilebilir durumda kalıyor.
<fileExtensions allowUnlisted="true">
<add fileExtension=".config" allowed="false" />
<add fileExtension=".bak" allowed="false" />
<add fileExtension=".backup" allowed="false" />
<add fileExtension=".old" allowed="false" />
<add fileExtension=".log" allowed="false" />
<add fileExtension=".sql" allowed="false" />
<add fileExtension=".mdf" allowed="false" />
<add fileExtension=".ldf" allowed="false" />
<add fileExtension=".cs" allowed="false" />
<add fileExtension=".vb" allowed="false" />
<add fileExtension=".csproj" allowed="false" />
<add fileExtension=".suo" allowed="false" />
<add fileExtension=".git" allowed="false" />
</fileExtensions>
.git uzantısını eklemek önemli. Git reposunu production’a deploy eden ve .git dizinini silmeyi unutan ekipler düşündüğünüzden fazla. Bu dizin erişilebilirse saldırgan kaynak kodunuzun tamamını indirebilir.
allowUnlisted="true" burada bilinçli bir tercih; yani listelemediğiniz uzantılara izin veriyorsunuz. Çok katı bir ortam istiyorsanız bunu false yapıp yalnızca ihtiyaç duyulan uzantıları (.html, .css, .js, .jpg vs.) tek tek izin verilebilir olarak tanımlayabilirsiniz. Bu whitelist yaklaşımı daha güvenli ama bakımı da daha zahmetlidir.
appcmd ile Komut Satırından Yönetim
IIS Manager’a erişiminiz yoksa ya da otomasyon scriptleri yazıyorsanız appcmd vazgeçilmezdir.
rem Belirli bir site için query string uzunluk limitini ayarla
%windir%system32inetsrvappcmd set config "SiteAdi" ^
-section:system.webServer/security/requestFiltering ^
/requestLimits.maxQueryString:1024 ^
/commit:apphost
rem Bir dosya uzantısını engelle
%windir%system32inetsrvappcmd set config "SiteAdi" ^
-section:system.webServer/security/requestFiltering ^
/+"fileExtensions.[fileExtension='.bak',allowed='false']" ^
/commit:apphost
rem TRACE metodunu engelle
%windir%system32inetsrvappcmd set config "SiteAdi" ^
-section:system.webServer/security/requestFiltering ^
/+"verbs.[verb='TRACE',allowed='false']" ^
/commit:apphost
/commit:apphost parametresi önemli. Bu, değişikliğin site-level web.config yerine applicationHost.config‘e yazılmasını sağlar. Böylece site yeniden deploy edildiğinde ayarlarınız kaybolmaz.
Hidden Segments ile Kritik Dizinleri Koruma
Hidden Segments özelliği, belirttiğiniz dizin isimlerini içeren herhangi bir URL’yi otomatik olarak 404 ile reddeder. İstemci bunun bir konfigürasyon kısıtlamasından mı yoksa gerçekten var olmayan bir sayfadan mı kaynaklandığını anlayamaz.
<hiddenSegments>
<add segment=".git" />
<add segment=".svn" />
<add segment="node_modules" />
<add segment="vendor" />
<add segment="App_Data" />
<add segment="App_Code" />
<add segment="bin" />
<add segment="obj" />
<add segment="packages" />
<add segment=".env" />
</hiddenSegments>
.env dosyalarını burada listelemenizi özellikle öneririm. Laravel, .NET Core ve pek çok modern framework environment değişkenlerini bu dosyada tutar. Yanlışlıkla web kök dizinine konan bir .env dosyası veritabanı şifrelerinizi, API anahtarlarınızı düz metin olarak açığa çıkarır.
PowerShell ile Toplu Konfigürasyon ve Doğrulama
Birden fazla siteyi yönetiyorsanız her şeyi elle yapmak hem yorucu hem de hataya açıktır. Aşağıdaki script sunucudaki tüm sitelere temel güvenlik konfigürasyonunu uygular ve sonrasında doğrulama yapar.
# Tüm IIS sitelerine Request Filtering güvenlik konfigürasyonu uygula
Import-Module WebAdministration
$sites = Get-Website
foreach ($site in $sites) {
$sitePath = "IIS:Sites$($site.Name)"
$configPath = "MACHINE/WEBROOT/APPHOST/$($site.Name)"
Write-Host "Configuring: $($site.Name)" -ForegroundColor Cyan
# Request limits
Set-WebConfigurationProperty -pspath $configPath `
-filter "system.webServer/security/requestFiltering/requestLimits" `
-name "maxAllowedContentLength" -value 30000000
Set-WebConfigurationProperty -pspath $configPath `
-filter "system.webServer/security/requestFiltering/requestLimits" `
-name "maxUrl" -value 2048
Set-WebConfigurationProperty -pspath $configPath `
-filter "system.webServer/security/requestFiltering/requestLimits" `
-name "maxQueryString" -value 1024
# Double escaping engelle
Set-WebConfigurationProperty -pspath $configPath `
-filter "system.webServer/security/requestFiltering" `
-name "allowDoubleEscaping" -value "false"
# Hassas uzantilar
$blockedExtensions = @('.config', '.bak', '.log', '.sql', '.mdf', '.ldf', '.git')
foreach ($ext in $blockedExtensions) {
try {
Add-WebConfigurationProperty -pspath $configPath `
-filter "system.webServer/security/requestFiltering/fileExtensions" `
-name "." `
-value @{fileExtension=$ext; allowed='false'}
} catch {
Write-Host " Already configured: $ext" -ForegroundColor Yellow
}
}
Write-Host " Done." -ForegroundColor Green
}
# Doğrulama
Write-Host "`n=== Verification ===" -ForegroundColor Magenta
foreach ($site in $sites) {
$filter = Get-WebConfigurationProperty `
-pspath "MACHINE/WEBROOT/APPHOST/$($site.Name)" `
-filter "system.webServer/security/requestFiltering" `
-name "allowDoubleEscaping"
Write-Host "$($site.Name) - allowDoubleEscaping: $($filter.Value)"
}
Gerçek Dünya Senaryosu: E-Ticaret Sitesinde Sertleştirme
Bir e-ticaret projesi için yaptığım sertleştirme çalışmasından pratik notlar paylaşayım. Site ASP.NET MVC üzerinde çalışıyor, ödeme sayfaları var, yani güvenlik kritik.
İlk taramada tespit ettiğim sorunlar:
- TRACE ve OPTIONS metodları açıktı
.configuzantısına erişim engellenememiş- URL uzunluğu limiti varsayılan 4096’da kalmış
App_Datadizini hidden segment olarak tanımlanmamış
Uyguladığım konfigürasyon sonrasında yaptığım penetrasyon testi senaryolarında hiçbir dizin geçişi girişimi sunucuya ulaşamadı. OWASP ZAP ile çalıştırdığım aktif tarama raporunda önceki duruma kıyasla yüksek öncelikli bulgular %80 azaldı.
Ama dikkat etmeniz gereken bir nokta var: Bu sertleştirme ayarları bazen meşru uygulama işlevselliğini etkileyebilir. Örneğin bazı API endpoint’leri PUT metoduna ihtiyaç duyabilir, bazı dosya yükleme işlemleri maxAllowedContentLength sınırına takılabilir. Bu yüzden her zaman önce staging ortamında test edin, logları izleyin, sonra production’a taşıyın.
404 ile 403 Arasındaki Fark: Güvenlik Perspektifi
Request Filtering, engellenen isteklere varsayılan olarak 404 Not Found döner, 403 Forbidden değil. Bu bilinçli bir güvenlik tasarımıdır. Saldırgana 403 döndürürseniz “bu kaynak var ama erişemiyorsun” mesajı verirsiniz. 404 ile “böyle bir şey yok” dersiniz ve saldırgan bilgi kazanamaz.
Ama bazen log analizi için hangi isteğin neden reddedildiğini bilmeniz gerekir. Bunun için IIS loglarına sc-substatus kolonunu ekleyin:
# IIS loglarına substatus kodunu ekle
Set-WebConfigurationProperty -pspath "MACHINE/WEBROOT/APPHOST" `
-filter "system.applicationHost/sites/siteDefaults/logFile" `
-name "logExtFileFlags" `
-value "Date,Time,ClientIP,UserName,ServerIP,Method,UriStem,UriQuery,HttpStatus,SubStatus,Win32Status,TimeTaken,ServerPort,UserAgent,Referer"
Request Filtering tarafından engellenen istekler, 404 durum koduyla birlikte substatus 404.7 (dosya uzantısı engellendi), 404.5 (URL sequence engellendi) veya 404.6 (HTTP metod engellendi) gibi kodlar üretir. Bu kodları log yönetim sisteminizde filtrelerseniz güvenlik olaylarını çok daha kolay izleyebilirsiniz.
Konfigürasyonun Kalıcılığını Sağlamak
CI/CD pipeline’ı olan ortamlarda en büyük risk, her deployment’ta web.config’in üzerine yazılmasıdır. Güvenlik ayarlarınızı kaybetmemenin birkaç yolu:
- Güvenlik konfigürasyonunu
applicationHost.configseviyesinde tanımlayın, site-level web.config’e bırakmayın - Deploy pipeline’ına konfigürasyon doğrulama adımı ekleyin
- IIS Configuration Backup özelliğini kullanarak düzenli snapshot alın
# IIS konfigürasyonunu yedekle
$backupName = "SecurityHardening_$(Get-Date -Format 'yyyyMMdd_HHmm')"
& "$env:windirsystem32inetsrvappcmd.exe" add backup $backupName
# Yedekleri listele
& "$env:windirsystem32inetsrvappcmd.exe" list backup
Sonuç
Request Filtering, IIS’in en az konuşulan ama en değerli güvenlik özelliklerinden biri. Doğru yapılandırıldığında WAF’ın yapacağı işlerin önemli bir kısmını, sıfır maliyet ve minimum performans etkisiyle kernel yakınlığında gerçekleştiriyor.
Başlangıç için şu sıralamayı öneririm: Önce allowDoubleEscaping="false" ve hassas dosya uzantısı engellemelerini uygulayın, bunlar risksiz ve etkisi büyük adımlardır. Sonra HTTP metodlarını kısıtlayın. Ardından URL ve query string limitlerini ihtiyaca göre ayarlayın. Hidden Segments’i yapılandırın. Her adımda logları izleyin.
Güvenlik bir ürün değil, bir süreçtir. Request Filtering bu sürecin IIS katmanındaki kritik parçasıdır ve doğru uygulandığında size hem koruma hem de denetlenebilirlik sağlar. Şu an production’da çalışan IIS sunucularınız varsa ve bu ayarları henüz yapmadıysanız, bugün başlayın.
