Çeşitli nedenlerle vermek zorunda kaldığım iki aylık aradan sonra, IIS 7.0 ile ilgili bir yazıyla dönmeye karar verdim.
IIS 7.0, daha önceki bazı yazılarımda anlatmaya çalıştığım gibi, tamamen modüler bir yapıdadır. Üzerine kuracabileceğimiz eklentilerle çok farklı ihtiyaçlara cevap verebilmesi mümkün olmuştur. İşte bu yazımda, bu eklentilerden, çok işinize yarayacağını düşündüğüm üç tanesinden bahsedeceğim.
Administration Pack
Bu eklenti, aslında IIS'e yeni bir özellik getirmiyor; zaten var olan, ancak grafik arayüzü olmayan bazı özelliklere arayüz sağlıyor. Bunlar:
- Request Filtering
- FastCGI configuration
- ASP.NET Authorization
- ASP.NET Error Pages
- Log reports
Bunların dışında bir de "configuration editor" gelmektedir. Detaylı bilgi için buraya tıklayınız.
Advanced Logging
Bu eklenti henüz beta, ancak yine de bahsetmek istedim. Bu eklenti ile, IIS loglarını bir adım öteye taşıyabiliyoruz. Beraberinde gelen pek çok yeni log alanının dışında, biz de yeni alanlar ekleyebiliyoruz (örneğin Request Header, Response Header veya Server Variables içinden istediklerimizi).
Yukarıda Advanced Logging ile tanımlı gelen bazı alanları görebilirsiniz. Bunların arasında CPU-Utilization gibi gerçekten çok faydalı olabilecek olanlar var. Yukarıda göremediğimiz ama ayrıca eklenebilen "W3WP-PrivateBytes", "RequestsPerSecond", "Proxy" gibi alanlar da bulunuyor. Bu eklenti ile ilgili detaylı bilgi için buraya tıklayınız.
Dynamic IP Restrictions
Bu eklenti de, Advanced Logging gibi, henüz beta seviyesindedir. IIS 7.0 ile gelen Request Filtering özelliği ile uygulamalarımızı pek çok saldırıdan koruyabiliyoruz. Ancak DoS (denial of service) saldırılarına karşı bir savunma mekanizmamız bulunmuyordu. İşte Dynamic IP Restrictions eklentisi ile tam olarak bunu yapabiliyoruz: Örneğin, bir IP'den belirli bir süre zarfında, belirli bir sayıdan fazla istek gelirse, o IP'yi bizim belirlediğimiz süre boyunca bloklayabiliyoruz. Yani neredeyse bir "firewall"un yapabildiği şeyleri biz IIS üzerinden ve arayüzünden yapabilir duruma geliyoruz.
Belirlediğimiz kriterlere uyan bir durum söz konusu olduğunda nasıl bir tepki verileceğini de biz belirleyebiliyoruz:
- Send 403 (Forbidden)
- Send 404 (NotFound)
- Abort Request (Close Connection)
Bu eklenti ile ilgili detaylı bilgi için buraya tıklayınız.
Yalnız bu noktada şunu unutmamak gerekir ki, IIS bir firewall değildir ve asla bir firewall'un yapabildiklerini yapamaz. Buna benzer modüller bizim uygulamalarımızı bir ölçüde koruyacaktır; ancak sadece HTTP protokolü üzerinden gelen saldırılardan koruyabilecektir. Diğer her türlü saldırı ve tehdide karşı, web sunucularımız mutlaka bir firewall'un arkasında olmalıdır.
CENK ISCAN
“ViewState”, ASP.NET’le tanıştığımız ve Türkçe’ye “görsel durum” şeklinde çevirebileceğimiz bir kavramdır. Pek çoğumuz, belki de farkında olmadan, yazdığımız uygulamalarda “ViewState” kullanmışızdır. “ViewState” temelde, bir sayfanın son durumunu, sunucuya gidiş-gelişlerde (postback) muhafaza edebilmemizi sağlar. ASP’de bunu kendi yazdığımız kodlarla sağlayabiliyorduk, ve çok sayıda nesne içeren sayfalarda bu oldukça zahmetli olabiliyordu.
“ViewState”, sayfamızdaki nesneler hakkındaki bu bilgileri, XML formatında ve şifrelenmiş bir şekilde gizli (hidden) bir alanda tutar:
<input type="hidden" name="__VIEWSTATE" value="dDwtNTI0ODU5MDE1Ozs+ZBCF2ryjMpeVgUrY2eTj79HNl4Q=" />
Tahmin edebileceğiniz gibi, sayfada ne kadar çok nesne varsa, bu bilgi de o kadar büyüyecektir. Bu bilgi, her istekte istemciyle sunucu arasında gidip geleceğinden, aşırı büyümesi performans sorunlarına neden olabilmektedir. Ancak bugün bahsedeceğim konu bu değil.
“ViewState”, “default” olarak açık gelir. Yani, herhangi bir sayfada veya bir nesnede bunu kullanmayacaksak bizim kapatmamız gerekir. Dolayısıyla, biz bunu kullandığımızın farkında bile olmadan aşağıdakine benzer hatalarla karşılaşabiliriz:
Server Error in '/' Application.
The state information is invalid for this page and might be corrupted.
Description: An unhandled exception occurred during the execution of the current web request. Please review the stack trace for more information about the error and where it originated in the code.
Exception Details: System.Web.HttpException: The state information is invalid for this page and might be corrupted.
Source Error:
[No relevant source lines] |
Source File: c:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\Temporary ASP.NET Files\server\9d854\d9a735\App_Web_qcftt.0.cs Line: 0
Stack Trace:
[FormatException: Invalid length for a Base-64 char array.]
System.Convert.FromBase64String(String s)
………
[ViewStateException: Invalid viewstate.
Client IP: 192.168.1.17
Port: 2818
User-Agent: Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.2)
ViewState: /wEPDwUJTOA+MEbtAOBUV/zE/HaGg==
Referer: …
Path: /Default.aspx]
[HttpException (0x80004005): The state information is invalid for this page and might be corrupted.]
System.Web.UI.ViewStateException.ThrowError(…)
……
|
Version Information: Microsoft .NET Framework Version:2.0.50727.3082; ASP.NET Version:2.0.50727.3082
Hata ana hatlarıyla yukarıdaki gibi olacaktır (gereksiz yer kaplamasın diye oldukça kısalttım). Ancak neden farklı şeyler olabilir. Aşağıdaki makalelerde olası nedenleri ve herbiri için çözüm önerilerini bulabilirsiniz:
Troubleshooting the "View state is invalid" error with ASP.NET
http://support.microsoft.com/?id=829743
Intermittent Invalid Viewstate Error in ASP.NET Web pages
http://support.microsoft.com/kb/555353
Geçtiğimiz günlerde, bu iki makalede anlatılanlar dışında bir nedenle daha karşılaştım: Sorun sadece bazı istemci makinelerinde ortaya çıkıyordu. Çeşitli araçlarla (NetMon) hem istemci tarafında hem de sunucu tarafındaki ağ trafiğini dinleyip incelediğimizde, ilk istekte sunucudan gelen “ViewState” ile, sonraki istekte istemcinin geri gönderdiği arasında farklar olduğunu tespit ettik. Bu tür durumlara genelde tarayıcılardaki bazı “add-on”ların veya istemci makinesindeki firewall/antivirüs gibi uygulamaların neden oluyordur. Ancak tüm olasılıkları tek tek elediğimiz halde sorun devam ediyordu.
Son olarak, network uzmanı bir arkadaşımdan yardım istedik ve bize istemcinin network kartı sürücüsünde bir sorun olabileceğini söyledi. Nitekim ilgili sürücüyü güncellediğimizde sorun çözüldü.
CENK ISCAN
Takip edenler bilir, genelde sadece link içeren yazılar yazmıyorum. Ancak bu sefer yazmam gerektiğini düşünüyorum.
ASP.NET 2.0 menü bileşenini, adından da anlaşılacağı üzere, sayfalarımıza “pop-out” menü eklemek için kullanabiliyoruz. Fare imleci, menü nesnelerinden birinin üzerine geldiğinde bir “alt menü” açılmasını sağlayabiliyoruz. Ancak, Internet Explorer 8.0’ın “Standards Mode”unda bu tür menülerin çalışmadığını göreceksiniz. Genel olarak, herhangi bir hata alınmıyor olsa da, açılması gereken alt menü açılmayacaktır.
İşte bu sorunu gidermek için bir güncelleme çıkarıldı:
http://support.microsoft.com/kb/962351
Bu güncelleme henüz “hotfix” aşamasında. Bu demektir ki, tam olarak bu sorunu yaşıyorsanız kurmanız gerekir. Aksi takdirde ASP.NET 2.0 SP3’ü beklemenizi öneririm. Evet, ASP.NET 2.0 SP3. Yani, bu sorun IE 8.0’da değil, ASP.NET 2.0’da düzeltildi. Dolayısıyla sunucunuza bu güncellemeyi kurarak kullanıcılarınızın, site ve uygulamalarınızı sorunsuz görüntülemeye devam etmelerini sağlayabilirsiniz.
Bu yazımda IE 8.0’ın “mode”larından kısaca bahsetmiştim.
CENK ISCAN
"Web farm" kavramını, IIS ve/veya web yazılım geliştirme işiyle uğraşmış neredeyse herkes duymuştur. IIS 6.0'dan itibaren bir de "web garden" kavramıyla tanışmış olduk. Bu yazımda bu kavramların ne olduğundan ve bunları kullanırken nelere dikkat etmemiz gerektiinden bahsedeceğim.
Web farm
Bu kavram, Türkçe'ye "sunucu çiftliği" olarak çevrilen kavramın bir türüdür. Yani "web sunucusu çiftliği" diyebiliriz. Aynı web uygulamalarını ve/veya web sitelerini, birden fazla makine üzerinde bulundurmak anlamına gelmektedir. Genellikle "Windows Network Load Balance (NLB)" veya başka bir yük dağıtım uygulaması ile, ilgili uygulamaya/siteye gelen istekler bu makinelere pay edilir. Böylece, nispeten daha "zayıf" veya "ucuz" donanımlarla daha çok iş yapılabilir. Ancak daha önemlisi, makinelerden birinde yaşanabilecek sorunlar, kullanıcılar farketmeden, istekler diğer makinelere otomatik olarak yönlendirilerek çözülmüş olacaktır.
Web garden
Bu kavramı Türkçe'ye "web sunucu bahçesi" diye çevirmek yanlış olmayacaktır. Bu kavramla, IIS 6.0'la beraber tanıştık. Bunun nedeni, bu kavramın "uygulama havuzu" (application pool) kavramıyla gelmiş olmasıdır. Biliyorsunuz, IIS 6.0 ve 7.0'de her uygulama için ayrı bir uygulama havuzu tanımlayabiliyoruz.Her bir uygulama havuzu için de - en az - bir tane "worker process", yani w3wp.exe çalışıyor. İşte "web garden" kavramı tam da bu noktada devreye giriyor: Biz istersek, aynı uygulamayı taşıyan birden fazla w3wp.exe çalıştırılmasını sağlayabiliyoruz.
IIS 7.0'de bu ayarı, uygulama havuzu özelliklerinde yapabiliyoruz:
Tıpkı "web farm"da olduğu gibi, "web garden"da da, gelen istekler w3wp.exe'ler arasında pay edilmektedir. Ancak burada birden fazla makine söz konusu olmadığı için, bunu kullanmanın ilk nedeni performans olamayacaktır. Tıpkı “web farm”da olduğu gibi, w3wp.exe’lerden birinde ortaya çıkabilecek bir sorun, diğerlerini etkilememiş olacaktır. “Web garden” kullanmanın bir diğer nedeni de, 32bit ortamlarda hafıza kullanımını artırmaktır. Buradaki yazımda bahsettiğim gibi, 32bit ortamlarda her bir ASP.NET uygulaması en fazla 800MB hafıza kullanabilmektedir. Eğer bizim uygulamamız daha fazlasına ihtiyaç duyuyorsa, “web garden” özelliği ile birden fazla w3wp.exe kullanılmasını sağlayabiliriz.
Dikkat etmemiz gerekenler
Neden “web garden” ve “web farm” kullanmak isteyebileceğimizden bahsettik. Ancak, her zamanki gibi, bunları kullanmamız bazı sorunları da beraberinde getirecektir:
- Eğer uygulamamızda “session” kullanıyorsa ve bunu “InProc” yani uygulama üzerinde tutuyorsak sorun yaşayabiliriz: Bir kullanıcıdan gelen ilk istek hangi sunucu ve hangi w3wp.exe tarafından yanıtlanırsa “session” onun üzerinde yaratılacaktır. Eğer aynı kullanıcının sonraki bir isteği, başka bir sunucu veya w3wp.exe’ye denk gelirse, “session” bulunamayacak ve kullanıcı sorun yaşayacaktır. Bu nedenle, “web farm” ve/veya “web garden” kullanacaksak, “session” bilgilerıini başka bir yerde tutmalıyız (state server veya SQL Server gibi).
- Eğer sayfalarımızda “viewstate” açıksa, “web farm” ortamında sorun yaşayabiliriz. Yukarıdakine benzer bir şekilde, sunuculardan birinden alınmıs bir “viewstate” bilgisi, başka bir sunucuya gidecek olursa “Invalid ViewState” benzeri bir hatayla karşılaşırız. Aşağıdaki makalelerde bu sorunun detaylarından bahsedilmektedir:
http://support.microsoft.com/kb/555353
http://support.microsoft.com/kb/829743
Çözüm için, aşağıdaki makaledeki yolu izlememiz gerekmektedir:
http://support.microsoft.com/kb/312906
- Bir diğer sorun da, yine sadece “web farm” ortamları için geçerli olan, ağ kullanımının artması sorunudur. Bu, istemci tarafında “caching” yapılamamasıyla ilgili bir durumdur. Aşağıdaki makalede bu durum IIS 5.0 özelinde anlatılmaktadır, ancak diğer versiyonlar için de geçerli bir durumdur:
http://support.microsoft.com/kb/922733
Çözüm, IIS 6.0’da oldukça kolaydır. Aşağıdaki makaledeki çözüm önerilerinden bir numaralı olanı uygulamamız yeterli olacaktır:
http://support.microsoft.com/kb/922703
SONUÇ
Özetlemek gerekirse, hem “web garden” özelliği, hem de “web farm” kullanımı, ihtiyaçlara bağlı olarak bize ciddi faydalar sağlayabilir. Ancak elbette ne yaptığımızı biliyor olmamız koşuluyla.
CENK ISCAN
Daha önce "2 Sayısının Önemi" başlıklı yazımda "maxConnection" parametresinden bahsetmiştim. Ancak orada bahsettiklerim genel olarak .NET 1.1 için geçerliydi. .NET 2.0'da, duruma bağlı olarak davranış biraz daha farklı olabiliyor. Bu parametrenin detaylarından ve ne işe yaradığından önceki yazımda bahsetmiştim. Bu nedenle bunları tekrarlamayacağım.
autoConfig
.NET 2.0'ın machine.config dosyasını inceleyecek olursanız, maxConnection parametresinin bulunmadığını görürsünüz. Bu parametrenin değeri, artık çalışma zamanında (runtime) otomatik olarak hesaplanmaktadır. Hesaplama şöyle yapılıyor:
İstemci uygulamaları: "Windows forms" ve "console" uygulamaları gibi istemci uygulamalarında değer yine 2'dir.
ASP.NET uygulamaları: ASP.NET uygulamalarında ise, 12*CPU sayısı olarak hesaplanmaktadır. Yani 4 işlemcili bir makinede, bir sunucu uygulaması için bu değer otomatik olarak 48 olarak tanımlanır.
"machine.config" içerisinde bu tanım aşağıdaki satırla yapılmaktadır:
<processModel autoConfig="true" />
Bu parametre, yani "autoConfig", sadece "maxConnection" değerini belirlemekle kalmaz. Aşağıdaki değerlerin hepsinin otomatik hesaplanmasını sağlar:
maxConnection
maxWorkerThreads
maxIoThreads
minFreeThreads
minLocalRequestFreeThreads
Burada iki önemli nokta buluyor: Birincisi, autoConfig parametresi sadece ASP.NET uygulamaları için geçerlidir. İkinci önermli nokta ise, bu parametreyi "false" olarak verdiğimizde, ASP.NET uygulamalarımızın bundan olumsuz etkilenmemeleri için yukarıdaki parametrelirin hepsini tek tek tanımlamamız gerekir. Bu tanımları yaparken aşağıdaki makaleyi kullanabiliriz:
Contention, poor performance, and deadlocks when you make Web service requests from ASP.NET applications
http://support.microsoft.com/?id=821268
Ayrıca, autoConfig parametresiyle ilgili detaylı bilgiyi de aşağıdaki linkte bulabilirsiniz:
processModel Element (ASP.NET Settings Schema)
http://msdn.microsoft.com/en-us/library/7w2sway1(VS.80).aspx
NOT: Önceki yazımda Internet Explorer ve diğer tarayıcıların da 2 bağlantı kullandıklarından bahsetmiştim. Ancak yeni nesil tarayıcılarda bu değerler artırılmıştır.
CENK ISCAN
Bu hata, genelde boş beyaz bir sayfada, oldukça iri puntolarla karşımıza çıkar. Başka bir açıklama içermez ve genellikle IIS'i yeniden başlatana kadar da kurtulamayız. Hangi nedenlerle bunu alırız? Nasıl önüne geçebiliriz?
"503 - Service unavailable" hataları, IIS'in http.sys isimli sürücüsü tarafından verilir. Yani çekirdek (kernel) seviyesinde alınırlar. Dolayısıyla, bu hatalarla ilgili kayıtlar IIS'in kayıtlarında değil HTTPERR loglarında bulunur ve detayları, güvenlik gerekçesiyle, kullanıcılara gönderilmez. HTTPERR loglarında, bu hatanın nedenine dair bir de tanım görebiliriz. Bu tanımların listesi aşağıdaki linkte detaylarıyla verilmiştir:
http://www.microsoft.com/technet/prodtechnol/WindowsServer2003/Library/IIS/55f71614-ef1b-4015-b9c8-a42c1e700c25.mspx?mfr=true
Ben bu yazımda, en çok rastladğımız nedenlerin neler olduğundan ve bunların nasıl çözüleceğinden bahsedeceğim.
AppOffline
Bize gelen destek çağrılarından "Service unavailable" hatası ile ilgili olanların çok büyük kısmının nedeni "AppOffline" durumudur. Yukarıdaki makalede de bahsedildiği üzere, bu, ilgili "uygulama havuzu"nun (application pool) "Rapid fail protection" nedeniyle kapatıldığı anlamına gelmektedir. Aslında durumun çözümü çok basittir: "IIS Manager"da ilgili uygulama havuzuna sağ tıklayıp başlatmak yeterli olacaktır. Ancak bu, "Rapid fail protection" tekrar devreye girip, havuzu tekrar kapatana kadar çözüm olacaktır.
Tam bu noktada "rapid fail protection" özelliğinin tanımını yapalım: Herhangi bir uygulama havuzuna ait bir "worker process"te (w3wp.exe) bir sorun olur da kapanırsa ("crash" olursa), IIS tarafından hemen bir yenisi açılır ve yeni gelen istekler ona yönlendirilir. Bu arada kapanan uygulamada çalışan istekler yarım kalacaktır ama en azından daha sonra gelen istekler sorunsuz bir şekilde yanıtlanmaya devam edecektir. Ancak, eğer çok kısa bir zaman zarfında bu kapanma sorunu çok sık oluşmaya başlarsa ne yapmak gerekir? İşte "rapid fail protection" tam olarak bu noktada devreye girer. Eğer sorun çok sık ortaya çıkmaya başlarsa, kapanan uygulamayı yeniden başlatmaz ("disable" eder) ve kullanıcıların hata görmesini sağlar. Görülecek hatayı sanırım tahmin etmişsinizdir: Service Unavailable. Bu durum gerçekleştiğinde olay günlüklerinde (event log) aşağıdaki gibi bir kayıt görürüz:
IIS 6.0:
Event Type: Error
Event Source: W3SVC
Event Category: None
Event ID: 1002
Date: 14.01.2009
Time: 11:11:20
User: N/A
Computer: Win2K3Server
Description:
Application pool 'DefaultAppPool' is being automatically disabled due to a series of failures in the process(es) serving that application pool.
IIS 7.0:
Log Name: System
Source: Microsoft-Windows-WAS
Date: 1/14/2009 11:07:16 AM
Event ID: 5002
Task Category: None
Level: Error
Keywords: Classic
User: N/A
Computer: Win2K8Server
Description:
Application pool 'DefaultAppPool' is being automatically disabled due to a series of failures in the process(es) serving that application pool.
IIS 6.0 ve 7.0'da, her bir uygulama havuzunun "rapid fail protection" ayarı vardır. Bu ayar, ön tanımlı olarak aşağıdaki şekilde tanımlıdır:
IIS 6.0:
IIS 7.0:
"Rapid fail protection" açık olduğunda (Enabled = True) - ki biz kapatmadıysak açıktır - bizi en çok ilgilendiren parametreler şunlardır: "Failure Interval (minutes)" ve "Maximum Failures". Bu iki parametre ile bir uygulama havuzunda ne kadar sürede kaç hata alınırsa yeniden açılmayacağı tanımını yapmış oluyoruz. Yani yukarıdaki ayarlarla, eğer bir havuz 5 dakika içinde 5 defa bir sorun nedeniyle kapanacak olursa, altıncı defa çalılştırılmayacaktır.
Bu ayarlarla ilgili benim önerim "rapid fail protection"ın kapatılması yönünde olmayacaktır. Ancak belki 5 dakikada 5 defa değil de, 1-2 dakikada 5 defa gibi bir değişiklik yapmayı tercih edebilirsiniz. Buna tamamen sizin önceliklerinize ve uygulamanıza göre karar vermeniz gerekir.
En başta da söylediğim gibi, ilgili havuzu yeniden başlatarak bu sorunu çözebiliriz. Ancak burada önemli olan uygulamanın neden kapandığını tespit etmek olmalıdır. Bunun için de, eğer uygulamamız ASP.NET 2.0 ise olay günlüklerinde nedenine dair bir kayıt bulma ihtimalimiz olacaktır. Aksi takdirde "memory dump" alarak sorunun nedenini araştırmak gerekebilir.
AppPoolTimer
AppOffline kadar çok olmasa da bu da çokça karşılaştığımız nedenlerden biridir.
Her bir uygulama havuzunun, http.sys sürücüsünde bir kuyruğu bulunmktadır. Gelen istekler, http.sys tarafından bu kuyruğa bırakılırlar ve sırası gelen, ilgili w3wp.exe tarafında buradan alınıp işlenir. Ancak bazı durumlarda, w3wp.exe o kadar meşguldur ki, kuyruktan yeni bir istek alamaz. İşte bir istek bu kuyrukta çok uzun süre bekleyecek olursa, HTTPERR logunda AppPoolTimer şeklinde kaydedilen bir 503 hatası alır. Bu sorunun çözümü için, ilgili uygulamanın neden ve neyle bu kadar meşgul olduğunu tespit etmek gerekir, ki bu da genelde "memory dump" analizi ile mümkün olabilir.
ConnLimit
Oldukça nadiren de olsa, IIS üzerindeki uygulamamıza gelecek olan istek miktarını kısıtlamak isteyebiliriz. Bunu yaptığımız takdirde (ki normalde böyle bir limit tanımlı değildir), bu limit aşıldığı anda yeni istekler "Service unavailable" yanıtı alacaklardır. Bunun AppOffline'dan en temel farkı şudur: AppOffline durumunda, biz sorunu giderene kadar tüm istekler 503 alır. Ancak ConnLimit'te, bazı istekler yanıtlanırken bazıları 503 alabilir. Hatta bir kullanıcı sayfayı güncelledikçe, bazen 503 alıp bazen almayabilir. Bunun çözümü, ortaya çıkma nedeninden tahmin edebileceğiniz gibi, limiti artırmak veya tamamen kaldırmak olacaktır.
SONUÇ
Diğer nedenler üzerinde durmayacağım. Herbiriyle ilgili bilgileri yukarıdaki linkte bulabilirsiniz.
Sadece bu hata mesajına özgü olarak değil, IIS üzerinde çalışan uygulamalarla ilgili yaşanan sorunlarlın detaylarını görebileceğimiz 3 temel yer vardır:
- IIS logları
- HTTPERR logları
- Olay günlükleri
Bu üç kaynaktan toplayacağımız bilgilerle, hemen her sorunun nedenini bulabiliriz. Ancak tabii ki sorunun nedenini bulmak her zaman sorunu çözebilmek anlamına gelmeyebilir.
CENK ISCAN
Daha önce iki yazımda, HTTP protokolünde kimlik doğrulama işleminin nasıl yapıldığından kısaca bahsetmiştim. Ancak gelen yoğun ilgiye istinaden bunun detaylarından bahsetme gereği duydum.
- Tüm istekler önce "anonim" gönderilir!
Tarayıcıdan ve karşıdaki web sunucusundan bağımsız olarak, normal koşullarda, sunucuya gönderilen ilk istek, hiçbir kimlik bilgisi içermez. Bunun da aslında çok basit ve mantıklı bir nedeni vardır: İstekte bulunduğumuz web sunucusunun, istekte bulunduğumuz kaynak için kimlik doğrulama isteyip istemeyeceğini nereden bilebiliriz? Cevap kolay, bilemeyiz. Bu nedenle tüm tarayıcılar, önce anonim olarak bir istekte bulunur. Aşağıda örnek bir isteğin başlık (header) bilgileri bulunuyor:
GET /default.aspx HTTP/1.1
Accept-Language: en-us
UA-CPU: x86
Accept-Encoding: gzip, deflate
User-Agent: Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.0; WOW64; .NET CLR 2.0.50727; .NET CLR 1.1.4322; NET CLR 3.0.04506; .NET CLR 3.5.21022;)
Connection: Keep-Alive
Host: www.contoso.com
Yukarıdaki satırlardan sadece ilk ve son satırlar zorunludur. Bu nedenle şu aşamada diğer satırların ne anlama geldiği üzerinde durmayacağım. İlk satırda, hangi "verb" ile istekte bulunduğumuz (ki burada GET kullanmışız; veri gönderiyor olsaydık POST olacaktı), görüntülemek istediğimiz sayfanın adını ve son olarak da kullanmak istediğimiz HTTP versiyonunu belirtiyoruz. Son satırda da, istekte bulunduğumuz sayfanın bulunduğu web sitesinin adını veriyoruz.
Yukarıdaki satırların hiçbiri, kimlik bilgisi içermez. Zaten başta da belirttiğim gibi, ilk istekler neredeyse hiçbir zaman kimlik bilgisi içermez.
Eğer istediğimiz sayfaya "anonim" olarak erişilebiliyorsa, sunucu bize içeriği gönderecektir. Ancak eğer sadece belirli kişiler görüntüleyebiliyorsa o sayfayı, sunucu bize aşağıdaki gibi bir yanıt döner:
HTTP/1.1 401 Unauthorized
Content-Length: 1656
Content-Type: text/html
Server: Microsoft-IIS/6.0
WWW-Authenticate: Negotiate
WWW-Authenticate: NTLM
X-Powered-By: ASP.NET
Date: Fri, 2 Jan 2009 06:38:47 GMT
Yukarıda görüldüğü gibi, bir "401 Unauthorized" hatası aldık. Gelen yanıtın içeriğinde bu hatanın tam olarak "HTTP Error 401.2 - Unauthorized: Access is denied due to server configuration." olduğunu görecektik. Burada önemli olan "WWW-Authenticate" başlıklarıdır. Bu başlıklarla sunucu bize hangi tür kimlik doğrulama yöntemlerini desteklediğini söyler. Burada "Negotiate" ve "NTLM" desteklediğini görüyoruz. Bunların dışında "Basic" ve "Digest" gibi türler de olabilirdi.
Bazen, yukarıda olduğu gibi, bir web sunucusu birden fazla tür kimlik denetimi destekleyebilir. Tarayıcı, duruma göre "kullanabileceği" en "güvenli" yöntemi tercih eder. Bu, eğer mümkünse "Negotiate", yani "Kerberos" veya "NTLM"dir.
Tarayıcımız yukarıdaki durum için "Negotiate" kullanmaya karar vermiş olsun. Şimdi bizim adımıza ikinci bir karar vermesi gerekmektedir: Bize kullanıcı adı ve şifre mi soracaktır, yoksa kendisi mi bu işi halledecektir? Buna karar verirken bakacağı yer (bu, Internet Explorer'da değiştirilebilen bir ayardır) istekte bulunulan sitenin "yerel intranet" sitesi olup olmadığıdır. Buna karar verirken de, önce o sitesnin kendisindeki "yerel intranet" siteleri listesinde olup olmadığına bakar. Burada yoksa adreste nokta (.) karakteri geçip geçmediğine bakar. Eğer geçmiyorsa yine "yerel intranet" sitesi olduğunu varsayar.
Eğer sonuç olarak "yerel intranet" sitesi olduğuna karar verirse, bize şifre sormaz ve geri kalan adımları kendisi halleder. Aksi takdirde bize bu aşamada kullanıcı adımızı ve şifremizi sorar ve biz giriş yaptıktan sonra aşağıdaki adımlara devam eder.
- İkinci defa 401 hatası aldık!
Devam etmeden önce, eğer "Basic" kullanacak olsaydık ne olurdu, bundan bahsedelim: "Basic" kimlik doğrulama metodunda, kullanıcı adı ve şifre direkt olarak sunucuya gönderilir. Tarayıcımız bunu bilemeyeceğinden bize mutlaka sorar. Sonra açık olarak (şifrelemeden) sunucuya gönderir (bu nedenle SSL'siz kullanımı önerilmez). Eğer ilgili kaynağa erişim hakkımız varsa, direkt olarak bu ikinci adımda sayfa içeriğini alırız. Yani işlem burada biter.
"NTLM" ve "Negotiate" yöntemlerinde (yani "integrated windows authentication" seçili olan durumda) şifrenin kendisi değil, bir "jeton" (token) gönderilir. Bu da iki aşamada gerçekleşir. İlk aşamada, yani bu adımda, tarayıcı, aşağıdakine benzer bir istekte bulunur:
GET /default.aspx HTTP/1.1
Accept-Language: en-us
UA-CPU: x86
Accept-Encoding: gzip, deflate
User-Agent: Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.0; WOW64; .NET CLR 2.0.50727; .NET CLR 1.1.4322; .NET CLR 3.0.04506; .NET CLR 3.5.21022;)
Authorization: Negotiate TlRMTVNTUAABAAAAAAAA4gAAAAAAAAAAAAAAAAAAAAAGAAXEAAAADw==
Connection: Keep-Alive
Host: www.contoso.com
Yukarıda da görüldüğü üzere, önceki isteğin aynısı yeniden gönderiliyor. Tek fark, daha öncekinde olmayan "Authorization" başlığının da bulunmasıdır. Burada, tarayıcımızın "Negotiate" kullanmaya karar verdiğini görüyoruz. Sonrasında gördüğümüz karakter dizisi ise bizim kullanıcı adımız, makine adımız ve bağlı olduğumuz alan adımızı içeriyor olacaktır. Buna yanıt olarak da web sunucusu aşağıdaki gibi bir tane daha 401 hatası dönecektir:
HTTP/1.1 401 Unauthorized
Content-Length: 1539
Content-Type: text/html
Server: Microsoft-IIS/6.0
WWW-Authenticate: Negotiate TlRMTVNTUAAAAAAAAAAAAAAAAAAVgOONPEAQ+EUPD
sYAAAAAAAAAAAIBAgFIAAAAaaaaaaaaAAAAAAAAAAAAAE4ARQBSAFMAAgAQAFAAQQBSAF
...
HQAqwerewqwerewqwerewqwAGMAcgBvAHMAbwAAAAAAAAAAAAAAbQAFACwAZQB4AHQAcg
BhAG4AZQB0ACQQQQQQQQQQQQQvAHMAbwBmAHQALgBjAG8AbQAAAAAA
X-Powered-By: ASP.NET
Date: Fri, 02 Jan 2009 07:48:51 GMT
Bu seferki 401 hatasının tam açıklaması ise şöyledir: "HTTP Error 401.1 - Unauthorized: Access is denied due to invalid credentials." Aslında bu da çok mantıklı, çünkü biz henüz şifre veya jeton (token) göndermedik. Web sunucunun bize gönderdiği "WWW-Authentıcate" başlığındaki değer de duruma göre ya "ticket granting ticket"tır, ya da "challenge"dır (NTLM mi, Kerberos mu kullanıldığına göre değişir). Bunların ne olduğunun detayları HTTP protokolü açısından bakıldığında bizi ilgilendirmediğinden burada bahsetmeyeceğim. İlgilenenler yazımın sonundaki makalelerde bu detayları bulabilirler.
- Sonunda başardık!
İkinci defa 401 hatası aldıktan sonra tarayıcımız bir istek daha gönderir:
GET / HTTP/1.1
Accept-Language: en-us
UA-CPU: x86
Accept-Encoding: gzip, deflate
User-Agent: Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.0; WOW64; .NET CLR 2.0.50727; .NET CLR 1.1.4322; .NET CLR 3.0.04506; .NET CLR 3.5.21022;)
Host: www.contoso.com
Connection: Keep-Alive
Authorization: Negotiate TlRMTVNTUAADAAAAGAAYAIIAAABmAWYBmgAAABQAFABY
AAAACgAKAGwAAAAMABABABABABABABAAAAAAAAAAAAAAAABBBBAAAAAABBBKztSI/+IDo
mk69k0ASQBEAEQATABFAEUAQWBYSAAEEVJHKDRSSAWEAGMARQBOAEsAMAA4ADHlK0Du5P
...
ALgBjAG8AbQAWEBXasdaweGHJSSDSDFhdgjd0AC4AbQAACCCCAAGSAFMAbwBmAHQALgBj
AG8AbQAIADAAMAAAAAAAAAAAAAAAADAAAIpoewZRe+AAAAAAAAAAAAAAAAAAAAAAAAAAA
usZrrr1AAAAAAAAAAD7FFSas/AASDDcfasshcp
Bu defa tarayıcımızın gonderdiği "Authorization" başlığı, Kerberos kullanılıyorsa bizim "ticket"ımız, NTLM kullanılıyorsa şifremiz ve sunucudan alınan "challenge"ın matematiksel bir karşımıdır. Sunucu bunu aldıktan sonra kontrol ettirir (kullanıcı yerel bir kullanıcı ise Windows'a değilse "Domain Controller"a sorar). Eğer kullanıcının bilgileri doğruysa, ve ilgili kaynağa erişim hakkı varsa istenilen içerik kullanıcıya gönderilir. Aksi takdirde, yine duruma göre bir tane daha 40 hatası gönderilir. Bu durumda, tarayıcımız bize yeniden kullanıcı adı ve şifre sorabilir veya 401 hatasını direkt olarak bize gösterebilir.
SONUÇ
Tüm bunları toparlayacak olursak, IIS loglarında göreceğimiz her 401 hatası, sorun olduğu anlamına gelmez. Sonraki iki isteğin yanıtını da kontrol etmemiz gerekir.
Bu arada bir dipnot: Aynı kimlik doğrulama mekanizması "proxy"ler için de geçerlidir, çünkü onlar da HTTP protoklünü kullanırlar. Yani, örneğin ISA Server loglarında göreceğimiz "407 - Proxy authentication required." hataları için de yukarıdakiler geçerlidir.
REFERANSLAR
401.1 and 401.2-Authentication Problems (IIS 6.0)
http://www.microsoft.com/technet/prodtechnol/WindowsServer2003/Library/IIS/8feeaa51-c634-4de3-bfdc-e922d195a45e.mspx?mfr=true
How IIS authenticates browser clients
http://support.microsoft.com/?id=264921
Kerberos authentication and troubleshooting delegation issues
http://support.microsoft.com/?id=907272
How to troubleshoot Kerberos-related issues in IIS
http://support.microsoft.com/?id=326985
CENK ISCAN
Bize gelen "yüksek CPU kullanımı" ve "yüksek hafıza kullanımı" sorunlarının çoğunun birkaç tane ortak nedeni olduğunu gördüm. Bu yazımda bunlardan birinin üzerinde duracağım.
.NET uygulamalarında "string" işlemleri
"string" (dizgi) veri tipi, özellikle web uygulamalarında en çok kullandığımız veri tiplerinin başında geliyor. Web uygulamalarında muhtemelen, diğer tüm yazılım türlerinden daha çok "string" işlemi yapıyoruzdur. "string" işlemi derken kastımız, kelimelerin birleştirilip cümleler yaratılmasıdır. Bunu yapmanın en basit ve hızlı (yazılım geliştirme açısından hızlı, çalışma zamanındaki durumundan aşağıda bahsedeceğiz) yolu "+=" birleştirme operatörüdür.
Basit bir örnek vermek gerekirse:
string tableStr = "<table>";
for (int i = 0; i < 1000; i++)
{
tableStr += "<tr><td>" + i.ToString() + "</td></tr>";
}
tableStr += "</table>";
Response.Write(tableStr);
Yukarıdaki kod parçası, toplam 1000 satırlık bir tablo yaratacaktır. Tam olarak bu şekilde olmasa da, pek çok uygulamamızda buna benzer işler yapmışızdır. Kodlaması kolay ve çok masum görünüyor, öyle değil mi? Ama değil! Neden masum olmadığını anlamak için, .NET'in "string" işlemlerini ve hafıza yönetimini nasıl yaptığına bakmak gerekecektir.
"string" veri tipi
String veri tipi, aslında sabit ebatlı bir veri tipidir. Yani, biz bu tipte bir değişken yarattığımızda, onun büyüklüğü kadar hafıza alanı ayrılır. Örneğin:
string tableStr = "<table>";
satırı ile, hafızadan toplam 7 karakter için gerekli alan ayrılacaktır. Sonraki satırda tanımlanan "i" isimli değişken, "tableStr"den hemen sonraki hafıza alanında yaratılacaktır; yani 8. alan dolmuş olacaktır. Döngünün içine girilip, aşağıdaki satıra sıra geldiğinde, elimizdeki 7 karakterlik alan yetersiz kalacaktır. İşte sorun tam olarak burada ortaya çıkmaktadır. Bu soruna çözüm olarak, .NET, tableStr nesnesinin bir kopyasını yaratır. Ancak bu sefer sonuna eklenecek "string"i ekler ve hafızada gerekli alanı bu toplam üzerinden ayırır. Döngü bir daha döndüğünde aynı işlemi tekrarlar. Böylece, yukarıdaki gibi bir örnekte, hafızada bulunan ama kullanılmayan toplam 1000 tane "string" nesnesi bulunur:
1. döngü: "<table><tr><td>0</td></tr>"
2. döngü: "<table><tr><td>0</td></tr><tr><td>1</td></tr>"
3. döngü: "<table><tr><td>0</td></tr><tr><td>1</td></tr><tr><td>2</td></tr>"
4. döngü: "<table><tr><td>0</td></tr><tr><td>1</td></tr><tr><td>2</td></tr><tr><td>3</td></tr>"
...
Yukarıda da görebileceğiniz gibi, GC devreye girene kadar hafıza da yer kaplayacak olan ciddi miktarda "string" nesnemiz yaratılmış olacaktır. Durumu göz önüne aldığımızda aslında .NET'in yaptığı çok da mantıksız bir çözüm değildir; ve az sayıda birleştirme işlerinde çok da işe yarayacak bir yöntemdir. Ancak bu örnektekine benzer durumlarda hem CPU hem de hafıza kullanımını ciddi oranda artıracaktır.
ÇÖZÜM
Bu kadar bariz ve büyük bir sorun varken, tahmin edebileceğiniz gibi, .NET buna bir çözüm de sunmaktadır: "StringBuilder" nesnesi. Yukarıdaki kodu yeniden yazarsak:
StringBuilder sb = new StringBuilder("<table>");
for (int i = 0; i < 1000; i++)
{
sb.Append("<tr><td>" + i.ToString() + "</td></tr>");
}
sb.Append("</table>");
Response.Write(sb.ToString());
StringBuilder nesnesi, esnek ebatlıdır. Dolayısıyla, her "append" işleminde yeni bir kopya yaratılmaz. Aynı nesnenin ebatı büyütülür (bu iş tam olarak böyle değildir elbette ama detayları şu anda konumuz olmadığından girmeyeceğim).
Aşağıdaki makalede yukarıdakine benzer basit bir örnek kod verilmiş ve aradaki farkın ne kadar korkunç olabileceği anlatılmıştır:
http://support.microsoft.com/kb/306822
Yine aşağıdaki linkte StringBuilder nesnesi ile ilgili daha detaylı bilgi bulabilirsiniz:
http://msdn.microsoft.com/en-us/library/system.text.stringbuilder.aspx
CENK ISCAN
Bu yazımda IIS 7.0 uygulama havuzlarındaki (application pools) yeniliklerden bahsedeceğim. Daha önce bahsettiğim özellikler kadar göz önünde olmasalarda, burada bahsedeceğim özellikler, eminim ki IIS 7.0'a geçmek için çok geçerli nedenler sağlayacaktır.
Uygulama Havuzları
IIS 6.0 (yani Windows 2003) ile gelen uygulama havuzu mantığı, bana göre, IIS açısından ciddi bir devrim olarak nitelenebilecek kadar önemli bir yenilikti. Temel mantık, web uygulamalarımızın işlendiği "process"leri bizim kontrol edebilme ve yönetebilmemiz üzerine kurulmuştu. Her bir havuzun ne sıklıkla yeniden başlatılacağından, hangi kullanıcı yetkileriyle çalışacağına kadar pek çok ayarı yapabiliyoruz. Ayrıca her bir havuz üzerinde çalışacak uygulamalara da biz karar verip, istediğimiz sayıda havuz yaratabiliyoruz.
Bu noktada bir ara not vermek istiyorum: IIS 6.0 veya 7.0'da yaratabileceğimiz havuz sayısı teorik olarak neredeyse sınırsızdır. Ancak elbette işletim sistemi ve donanımımızın kısıtlamaları olacaktır. Bununla ilgili şunu söyleyebilirim, yeterli donanımla 500'den uygulama havuzunun başarılı bir şekilde çalıştırıldığını gördüm.
IIS 6.0'daki bazı kısıtlamalar
IIS 6.0'da çokca şikayet edilen birkaç tane kısıtlama vardı. Bunların ne olduklarına ve IIS 7.0'de nasıl giderildiklerine bir göz atalım:
- IIS 6.0'ı, eğer 64bit bir makinede kurulu ise, istersek 64bit olarak, istersek de 32bit olarak çalıştırabiliyorduk. Bunu Metabase'deki "Enable32BitAppOnWin64" parametresi ile belirleyebiliyorduk. Ancak bu parametre sadece web sunucu seviyesinde tanımlanabiliyordu. Yani, IIS, ya tamamen 64bit çalışıyordu, ya da tamamen 32bit. Örneğin, 64bit bir sunucu üzerinde hem ASP.NET 1.1, hem de ASP.NET 2.0 uygulamaları çalıştırmak istediğimizde, ASP.NET 1.1'in 64bit versiyonu olmadığından, tüm uygulamalarımızı 32bit çalıştırmak zorundayız.
IIS 7.0'da, bu özellik artık uygulama havuzu seviyesinde tanımlanabiliyor. Yani aynı sunucu üzerinde bazı uygulamaları 64bit olarak, bazılarını 32bit olarak çalıştırma şansımız bulunuyor.
- IIS 6.0'da, herhangi bir ASP.NET uygulamasının versiyonunu, uygulama üzerinde tanımlayabiliyoruz. Ancak, aynı uygulama havuzu üzerinde hem 1.1 hem 2.0 uygulamalar çalıştıramayız. Buna, uygulamaları ve üzerinde çalışakları havuzları tanımlarken bizim dikkat etmemiz gerekiyor.
IIS 7.0'da ise, bu tanımı da uygulama havuzu seviyesinde yapıyoruz. Bu da olası sorunları en aza indirmemize yardımcı oluyor.
- IIS 6.0'ın, "IIS 5.0 Isolation Mode" isimli bir çalışma modu bulunuyor. Bu, adından da anlaşılabileceği gibi, IIS'in tam olarak 5.0 gibi çalışmasını sağlıyor. Hatırlayacağınız gibi, IIS 5.0'da uygulama havuzlar yoktu; mimari olarak tamamen farklıydı. Bu yüzden, IIS üzerinde bu modu açtığımızda IIS tamamiyle 5.0 gibi çalışacaktır. Yani hem uygulama havuzlarımız olsun, hem de bazı uygulamalar "IIS 5.0 Isolation Mode"da çalışsın deme şansımız bulunmuyor.
IIS 7.0'da da, IIS 6.0 gibi çalışma seçeneğimiz bulunuyor. Ve bunu uygulama havuzu seviyesinde tanımlayabiliyoruz. Her uygulama havuzunun "Managed Pipeline Mode" isimli bir özelliği bulunuyor. Bunu "integrated" seçersek, o havuz IIS 7.0'la gelen "integrated pipeline" modunda çalışacaktır. Eğer bu ayarın alabileceği diğer değer de "classic"dir. Bu modu, IIS 6.0 modu gibi düşünmek yanlış olmaz. Yukarıda da söylediğim gibi, bu ayar da uygulama havuzu seviyesinde yapılan bir ayardır.
Yukarıdaki değişiklik ve yeniliklerin dışında, bir de daha önce grafik arayüzden yapamadığımız ayarlar var. Herhangi bir uygulama havuzunun "gelişmiş özellikler"ini (Advanced settings) açtığınızda, IIS 6.0'dakinden çok daha fazla özellik göreceksiniz. Bunların çoğu aslında IIS 6.0'da da ayarlayabildiğimiz özelliklerdi. Ancak bunları arayüzden yapamıyorduk. Özel olarak varlıklarını bilmemiz ve bunu metabase içerisinde tanımlamamız gerekiyordu. Bunlara birkaç örnek vermek gerekirse:
- LoadBalancerCapabilities: "Service Unavailable" hatası dönülmesi gereken durumlarda bunun ne şekilde yapılacağı ayarıdır. "Load balancer" kullandığımız durumlarda, onun desteklediği seviyeye göre ayar yapabiliriz.
- logEventOnRecycle: IIS, bir uygulama havuzu bazı nedenlerle yeniden başlatıldığında olay günlüklerine (event logs) bununla ilgili bir kayıt düşer. Hangi nedenlerle yeniden başlatıldığında kayıt düşülmesini istediğimizi bu parametre ile ayarlıyoruz.
- orphanWorkerProcess: IIS 6.0 ve 7.0'da, bir "worker process", yanıt veremez duruma gelirse, o kapatılıp yerine yenisi açılır. Eğer biz, o "worker process"in neden cevap veremediğini araştırmak istersek, o "process" kapatılmadan yenisi açılsın şeklinde bir tanımlama yapabiliriz.
SONUÇ
Bugüne kadar IIS 7.0 hakkında yazdığım yazılarda, yeni ve geliştirilmiş özelliklerden bazılarına değinmeye çalıştım. Bunları anlatırken önceliği faydalı olacağını düşündüğüm ve verdiğim eğitimlerde beğenildiğini gördüğüm özelliklere verdim. Ancak bu 3 yazımda bahsettiklerim toplamda IIS 7.0'nin yeni ve faydalı özelliklerinin yine de küçük bir kısmıdır. Benim naçizane önerim, en kısa zamanda IIS 7.0 ile testlere başlamanız ve en kısa zamanda uygulamalarınızı taşımanız olacaktır. Her ne kadar biraz reklam kokan bir cümle de olsa, şahsen ben pişman olacağınızı düşünmüyorum.
CENK ISCAN
IIS 6.0, önceki versiyonlara göre çok ciddi mimari değişikliklere sahipti. Bunlardan bir tanesi de HTTP.SYS isimli bir "sürücü" idi. IIS'in daha eski versiyonlarında çekirdek (kernel) seviyesinde çalışan bir bileşen yoktu.
IIS 6.0 (ve 7.0) ile gelen bu bileşenin neler yaptığının çok fazla detayına (en azından bu yazımda) girmeyeceğim. Burada bahsetmek istediğim, bu mimari değişikliği sonucu ortaya çıkan ikinci bir tür IIS logudur: HTTPERR logları.
HTTPERR logları
IIS loglarını kaydeden bileşen HTTP.SYS sürücüsüdür. Gelen isteği, ilgili uygulama havuzuna (application pool) gönderir ve oradan bir yanıt geldiğinde (istek başarılı da olsa başarısız da olsa) bunu IIS'in loglarına kaydeder. Ancak bazı durumlarda uygulama havuzundan yanıt alamayabilir; isteği aktaracaği uygulama havuzunu bulamayabilir; veya istekteki bir sorun nedeniyle kendisi bloklamaya karar verebilir. Bu tur istekleri IIS loglarına kaydetmez (kaydedemez). Ancak bunlarla ilgili bilgileri de tutmak gerekir. İşte tam da bu noktada HTTPERR logları devreye girer.
Yukarıdaki tanımdan da anlaşılabileceği gibi, IIS'e kadar ulaşabilmiş her istek mutlaka loglanır: IIS loglarına değilse bile HTTPERR loglarına. Bu nedenle, bazı sorunları incelerken IIS loglarını inceler ve orada ilgili isteğin kaydını bulamazsak bir de HTTPERR loglarına bakmalıyız. Eğer burada da yoksa, istek IIS'e hiç ulaşmamış diyebiliriz.
Loglarda neler görebiliriz?
Aşağıdaki makalede bu loglarda ne gibi bilgilerin tutulduğu ve hangi mesajın ne anlama geldiği bilgilerini bulabilirsiniz:
Error logging in HTTP API
http://support.microsoft.com/kb/820729
Bu makalede her turlu detay anlatıldığı için ben burada yeniden anlatmayacağım. Benim burada eklemek istediğim bir nokta, hata mesajları arasında Timer ile başlayanların genelde aslında sorun belirtmeyeceğidir. Bunları (genel olarak) göz ardı edebiliriz. En çok karşılaşacağımız ve gerçekten sorun belirtiyor olabilecek olanlardan bazıları şunlardır:
AppOffline - Uygulama havuzunun (application pool) kapatıldığı ve kullanıcının "Service Unavailable" aldığı anlamına gelir. Olay günlüklerinde (event log) bunun nedeniyle ilgili daha detaylı bilgi bulunabilir.
Connection_Abandoned_By_AppPool - Uygulama havuzu beklenmedik bir şekilde sonlanmıştır. Buna genellikle uygulamadaki bir sorun neden olur. Olay günlüklerinde (event log) bunun nedeniyle ilgili daha detaylı bilgi bulunabilir. Daha fazla detay için için dump analizi gerekebilir.
Connections_Refused - Kernel NonPagedPool hafıza alanında boş miktar 20MB'ın altına düşmüstür. Bu alan, özel bir hafıza alanı olup ebatı oldukça sınırlıdır. Burayı genellikle "sürücüler" kullanır. Bunlardan birinde oluşabilecek bir sızıntı, sonuçta IIS'in yanıt veremez hale gelmesine neden olabilir. Bu sorunu atlatmanın yolu makineyi yeniden başlatmaktır. Tamamen gidermek için hangi sürücünün soruna neden olduğunu tespit etmek ve gerekiyorsa o sürücüyü güncellemek gerekecektir.
CENK
Geçen ay ilkini yazdığım bu serinin ikinci yazısında, aslında (tam olarak bu şekilde çalışmasa da) IIS 6.0 SP1 ile gelmiş olan ama pek bilinmeyen bir özellikten bahsedeceğim: "Failed Request Tracing" (FRT veya FREB)
Failed Request Tracing
Adından da anlaşılacağı üzere, bu özellik, başarısız olmuş isteklerle ilgili detayların kaydedilmesini sağlamaktadır. IIS 7.0'de, bunu tamamen arayüz üzerinden yapabiliyoruz. Ayrıca "başarısız istek" tanımını da kendimiz yapmaktayız. Aşağıdaki ekran görüntülerinde, tam olarak neler yapabildiğini güreceksiniz:
FREB'i aktive edebilmek için öncelikle, hangi web sitesinde açacaksak sol tarafta o web sitesini seçelim. Sağdaki "Actions" menüsünde "Failed Request Tracing..." linkini göreceksiniz:
Bu linke tıklayınca açılan pencereden öncelikle FREB'i aktive etmemiz gerekmektedir. Yine bu pencereden, toplam kaç kayıt dosyası istediğimizi ve bunların nereye kaydedilmesini istediğimizi belirliyoruz:
Kayıt dosyası sayısında bir kısıtlama olmasının nedeni, az sonra belirleyeceğimiz kriterlere göre başarısız olan her istek için bir dosya yaratılıyor olması ve bu dosyanın ebatının 100-200 KB cıvarında olmasıdır. Eğer bu sayıyı çok yükseltirsek, ciddi performans sorunlarıyla karşılaşabiliriz. Burada yeri gelmişken şunu belitmekte fayda var: FREB gibi her türlü "tracing" işinin bir de maliyeti vardır. Bu nedenle bu tür özellikleri açmadan önce bunu mutlaka göz önünde bulundurmak gerekir. Ayrıca işimiz bitip sorunu tespit ettikten (ve giderdikten) sonra mutlaka tekrar kapatın.
FREB'i aktive ettikten sonra, orta bölümdeki "Failed Request Tracing Rules" ikonuna tıklayın. Burada açılacak olan sihirbaz ile "başarısız istek" kreiterlerimizi tanımlıyacağız:
Yukarıda da görebileceğiniz gibi, hangi tür dosyaların izleneceğini belirleyebiliyoruz. Dahası, eğer sorunun tam olarak hangi sayfada olduğunu biliyorsak sadece o sayfayı da izlemeye alabiliriz.
İkinci adımda "başarısız istek" kriterlerimizi belirliyoruz. Bu kriterler, HTTP durum kodları ve/veya isteğin işlenme süresi ve/veya olayın "aciliyeti" olabilir. Aşağıdaki örnekte 500 hata kodu dönen isteklerle toplamda 30 saniyeden daha uzun süren istekler için kayıt tutulacaktır:
Son adımda, tutulacak kaydın detay miktarını belirliyoruz. İzlediğimiz uygulama türüne göre seçimimizi yaparsak performans kaybımızı bir miktar azaltabiliriz:
Bu tanımı yaptıktan sonra, bu web sitesinde, 500 hata koduyla sonuçlanan veya toplam işlenme süresi 30 saniyeyi aşan tüm istekler için detaylı kayıtlar tutulacaktır. Burada daha önceden topladığım kayıtları paylaşmayacağım (olası içerikleri nedeniyle). Ancak size test amaçlı olarak bazı loglar toplamanızı ve incelemenizi öneririm.
Bu özellik, "dump analizi" ihtiyacımızı tamamen sıfıra indirmeyecek olsa da, pek çok durumda işimizi kesinlikle çok kolaylaştıracaktır.
CENK ISCAN
URLScan Nedir?
URLScan, IIS'in işleyeceği HTTP isteklerini kontrol edip gerektiğinde engellemeye yarayan bir güvenlik aracıdır. IIS üzerinde bir "ISAPI Filter" olarak çalışır ve filtreleme işini bizim belirlediğimiz kriterlere göre yapar. Biz bu kriterleri "urlscan.ini" isimli dosyada tanımlarız. Bu blogda URLScan'in teknik detaylarından bahsetmeyeceğim. Ancak aşağıdaki linklerde çok detaylı bilgi bulabilirsiniz:
http://learn.iis.net/page.aspx/473/using-urlscan/
http://learn.iis.net/page.aspx/475/urlscan-setup/
SQL Injection nedir?
SQL Injection, en basit tanımıyla, uygulamamızdaki SQL cümleciklerine dışarıdan müdahale edilmesidir diyebiliriz. Buna izin veren (ya da vermeyen) bizim yazdığımız uygulamanın kendisidir. Bilmeyenler olabilir diye kısaca saldırının nasıl işlediğini anlatalım:
Diyelim ki bir web uygulamanız var ve bunu kullanmak için kullanıcı adı ve şifre girilen bir sayfanız var. Anlatım kolaylığı açısından uygulamamız ASP uygulaması olsun. Şimdi, bir kullanıcı, kullanıcı adını ve şifresini girip "Giriş" butonuna tıkladığında, ASP sayfamızda şuna benzer bir kod çalışıyor olacaktır:
<%
conn=Server.CreateObject("ADODB.Connection");
connStr = "...";
conn.Open (connStr);
rs = Server.CreateObject("ADODB.Recordset");
sqlStr = "SELECT * FROM Users WHERE username='" + Request("kullaniciAdi") + "' AND password='" + Request("sifre") + "'";
rs = conn.Execute(sqlStr);
%>
sqlStr'nin tanımlandığı satırda, ilgili değişkenler yerine konduğunda veritabanına gönderilecek SQL cümleciği şöyle birşey olacaktır (kullanıcı adının CENK ve şifrenin de İRDEN olduğu bir senaryada):
SELECT * FROM Users WHERE username='CENK' AND password='İRDEN'
Kodun geri kalanında, eğer bu SQL cümleciği bir sonuç döndürmüşse kodumuz çalışmaya devam edecektir. Buraya kadar herşey normal görünüyor. Ancak eğer birisi kullanıcı adı yerine aşağıdaki gibi birşey girerse ne olur:
' OR 1=1 --
Bu durumda SQL cümleciğimiz şöyle bir hal alır:
SELECT * FROM Users WHERE username='' OR 1=1 --' AND password=''
Bilmeyenler olabilir diye açıklamakta fayda var: "--" işareti, SQL'da, "satırın geri kalanını dikkate alma" demektir. Dolayısıyla yukarıdaki cümlenin çalıştırılacak kısmı
SELECT * FROM Users WHERE username='' OR 1=1
olacaktır. Yani, kullanıcı adı boş olan VEYA 1=1 olan durumlarda sonuç dönecektir. 1=1 her koşulda doğru bir önerme olduğuna göre, veritabanımız bize tüm kullanıcıların listesini dönecektir. İşte tam da bu noktada, kullanıcı adı ve şifre olmadan da uygulamamıza girilmiş olur. Buradan sonrası kodumuzla neler yapılabildiğine ve saldırganın insafına kalmıştır.
Sadece kullanıcı girişinde değil, kullanıcıdan girdi aldığımız her yerde buna benzer sıkıntılarla karşılaşabiliriz. Yani sisteme bir girişle sonuçlanmsa da, bazı durumlarda bu saldırı bir hataya neden olur. Bu hatanın içerisinde bizim için kritik bazı bilgileri de (örneğin veritabanı veya tablo adı, hatta veritabanı erişim şifresi gibi) saldırganla paylaşıyor olabiliriz.
SQL Injection saldırılarından korunmak aslında çok kolaydır. Tek yapmamız gereken, kullanıcıdan aldığımız her türlü veriyi (bizim örneğimizde kullanıcı adı ve şifreyi) veritabanına göndermeden önce kontrol etmektir. İçinde tek tırnak geçenleri engellemek bile aslında yeterli olabilir ama yine de daha detaylı bir kontrolden geçirmek daha doğru olacaktır.
URLScan kullanarak SQL Injection saldırılarını nasıl önleriz?
URLScan kullanarak, saldırganların uygulamamıza belirli kelime ve karakter öbeklerini göndermelerini engelleyebiliriz. Bunun için, urlscan.ini dosyasında aşağıdaki gibi bir tanım yapmamız yeterli olacaktir:
[Options]
RuleList=SQL Injection
[SQL Injection]
AppliesTo=.asp,.aspx
DenyDataSection=SQL Injection Strings
ScanUrl=0
ScanAllRaw=0
ScanQueryString=1
ScanHeaders=
[SQL Injection Strings]
--
%3b
/*
@
char
alter
begin
cast
convert
create
cursor
declare
delete
drop
end
exec
fetch
insert
kill
open
select
sys
table
update
Yukarıdaki tanım, sadece "query string"leri tarayacağından, POST metodunu kullandığımız sayfalarda işe yaramacaktır. İstersek "ScanAllRaw=1"tanımını yaparak isteğin tamamının taranmasını sağlayabiliriz. Ancak bu noktada da, güvenlikle ilgili her konuda olduğu, denge sorunu devreye giriyor. Eğer tüm "header"ları taratacak olursak, muhtemelen hiç kimse uygulamamıza erişemeyecektir. Çünkü pek çok isteğin "header"ları arasında yukarıdaki listedeki en az bir öbek geçiyordur. Güvenlikle kullanılabilirlik arasındaki hassas dengeyi doğru kurmaya dikkat etmemiz gerekir.
NOT: Yukarıdaki tanımı aşağıdaki blogdan aldım. Burada URLScan'in kullanımıyla ilgili başka örnekler de bulabilirsiniz. Hatta URLScan loglarının LogParser kullanarak incelenmesine dair örnekler de bulunuyor:
http://learn.iis.net/themes/iis/pages/page.aspx/476/common-urlscan-scenarios/
SONUÇ:
- SQL Injection saldırıları, günümüzde web sitelerinin en çok maruz kaldığı saldırı türüdür demek yanlış olmaz. Aslında bunlardan korunmak oldukça kolaydır. Asıl yapılması gereken kodlarımızda kullanıcı girdilerini kontrol etmek olmalıdır. URLScan de bize bu noktada bir korunma katmanı daha sağlayabilir.
- URLScan, SQL Injection saldırılarının ötesinde, pek çok açıdan ciddi bir güvenlik katmanıdır. Neler yapabildiğini detaylı olarak incelemekte fayda olacaktır.
CENK ISCAN
IIS 7.0, pek çok yeni özellikle beraber geldi. Bunların bazılarından kısaca daha önce bahsetmiştim. Ancak benim özellikle çok faydalı bulduğum bazı özelliklerinden daha detaylı bahsetmek istedim. İşte bunlardan ilki:
IIS Manager Users
IIS'in daha önceki versiyonlarında, IIS yönetimi yapabilmek için bir kullanıcının enazından yerel yönetici (local administrator) olması gerekiyordu. IIS 7.0'de ise yönetim işi tamamen farklı bir mantıkla yapılıyor.
Yerel yönetici olan kullanıcılar yine IIS üzerinde tam yetkiye sahiptirler. Ancak eğer istersek, yerel yönetici olmayan kullanıcılara da bir takım yetkiler verebiliriz. Örneğin belirli bir sitenin tüm yönetim işlerini bir kullanıcıya verebiliriz. Bu kullanıcı, IIS konsoluna bağlandığında, sadece yetkili olduğu web sitesini ve onun özelliklerini görecektir. Diğer web siteleri ve IIS'in genel ayarlarını göremeyecek ve dolayısıyla değiştiremeyecektir. Hatta web sitesi değil, web uygulaması bazında bile yapabiliriz bunu.
Yine bir diğer yenilik de, yukarıda bahsettiğimiz kullanıcıların Windows kullanıcısı olmak zorunda olmamasıdır. Artık sadece IIS'e özgü kullanıcılar yaratıp, yetkilendirmeyi bunlar üzerinden yapabiliyoruz. Aslında düşününce bu özellik pek proje grubunun çok işine yarayacaktır. Ancak eminim bunu en çok kullanacak olanlar "web hosting" firmaları olacaktır.
Aşağıda yeni bir kullanıcı yaratmak ve kullanıcıya yetki vermekle ilgili ekran görüntülerini bulacaksınız:
Yeni kullanıcı ekleme:
Kullanıcı listesi:
Bir site veya uygulamaya yetkili lullanıcı ekleme (aşağıdaki ekran görüntüsünde de görüldüğü gibi ister IIS üzerinde yarattığımız kullanıcıları, ister Windows kullanıcılarını kullanabiliriz):
Herhangi bir web sitesi veya uygulamasında hangi kullanıcıların yetkileri olduğunu "IIS Manager Permissions" arayüzünden görebiliriz:

CENK ISCAN
Son günlerde çokça karşıma çıkan bir virüsten (aslında sanırım bir truva atı) bahsetmek istiyorum. Aslında virüsün çalışma şeklinin ne IIS'le ne de ASP.NET'le bir ilgisi yok. Ancak, web sitelerine erişimde sorun yarattığından sorun yaşayanlar önce bize geldiler ve bu virüsten bu şekilde haberim oldu. Bu açıdan (örneğin 401 hatasına neden olabiliyor) aslında burada bahsettiğim virüsle benzerlikler göstermektedir.
Virüsün teknik detaylarını, nasıl çalıştığını, nasıl bir güvenlik sorununa neden olduğunu bilmiyorum. Internet üzerinde bununla ilgili çok fazla detay bulamadım. Ancak, bizim yaptığımız incelemelere göre, bir makineye bulaştıktan sonra, makinenin bağlı bulunduğu ağdaki "ağ geçidinin" (gateway) MAC adresini "spoof" ediyor. Yani kendisini ağ geçidi gibi gösteriyor. Bu şekilde ağdaki diğer makinelerin trafiğinin kendisi üzerinden geçmesini sağlıyor. Bunu yaptıktan sonra da, kendi üzerinden geçen HTTP trafiğini bozuyor. Bozmaktan tam olarak kastım, bazı "header" bilgilerini siliyor ve HTML kodunun en başına şuna benzer bir satır ekliyor:
<script language="javascript" src="http://v.freefl.info/day.js"></script>
Dolayısıyla, ağdaki herhangi bir makineden birisi Internet'te herhangi bir sayfaya ulaşmaya çalıştığında "days.js" isimli dosyayı da indirmiş oluyor. Bu dosyanın içeriğini tam olarak bilemiyorum. Ben indirmeye çalıştığımda 404 hatası alıyorum. Ancak başka sunucular/adresler kullanarak virüsün yayılmaya devam etmesi olası görünüyor.
ÇÖZÜM:
Bu virüsü, bu yazıyı hazırladığım sırada, tanıyabilen çok fazla antivirüs uygulaması bulunmuyordu. Ancak tahmin ediyorum yakın zamanda tüm antivirüsler tanıyabilecektir. Burada önemli olan nokta şu: Bizim sorunu gözlemlediğimiz makine, çoğu zaman aslında enfekte olan makine olmayacaktır. Virüs, ağdaki herhangi bir makinede bulunuyor olabilir. Belki ağ paketlerini izleyerek hangi makinede olduğunu bulabiliriz. Ama bence her koşulda ağda genel bir tarama yapmak daha doğru olacaktır.
CENK ISCAN
Bu blogun konusu IIS ve web yazılımı geliştirme olduğu halde, geliştirdiğimiz uygulamaların görüntüleneceği ortam hakkında da bazen yazmak gerekebiliyor.
Bu yazı hazırlandığı sırada IE8 henüz çıkmamıştı; şu anda Beta 1 sürümünü indirip kullanabiliyorsunuz. Elbette, Beta seviyesindeki herhangi bir yazılım gibi, bazı sorunlar yaşayabilirsiniz. Ancak Microsoft olarak, en azından bir test makinesine IE8 kurmanızı ve web sitelerinizi/uygulamalarınızı bunun üzerinde test etmenizi öneriyoruz.
IE8, en yeni HTML ve CSS standartlarına uyumlu olarak geliştirilmektedir. Bu da, IE7'de düzgün olarak görüntülenebilen bir sayfanın, IE8'de bozuk taranmasına neden olabilecektir. İşte tam da bu nedenle var olan web sitelerimizi ve uygulamalarımızı, IE8 piyasaya çıkmadan test etmekte fayda olacaktır.
Bu testler sonucunda sayfalarımızın bozuk görüntülendiğini farkedersek ne yapacağız? Elbette asıl yapılması gereken sayfalarımızın kodunu yeni standartlara uyumlu hale getirmek olmalıdır. Ancak bu zaman alabilir. İşte bu süre zarfında kullanıcılarımızın bundan etkilenmesini engellemek için, IE8'in, EmulateIE7 modunu kullanabiliriz. Bununla ilgili aşağıdaki makaledeki adımları izlememiz gerekecektir:
Web siteniz Internet Explorer 8 Beta 1'de düzgün görüntülenmeyebilir
http://support.microsoft.com/kb/952030/tr/
IE8 Beta 1 sürümünü de, yukarıdaki makalede de verilmiş olan, aşağıdaki linkten indirebilirsiniz:
http://www.microsoft.com/windows/products/winfamily/ie/ie8/getitnow.mspx
NOT: Yeri gelmişken IE8'de benim oldukça ilgimi çeken iki yeni özelliği de paylaşmak isterim:
WebSlices ve Activities
http://www.microsoft.com/windows/products/winfamily/ie/ie8/features.mspx
CENK ISCAN