.NetGurus

Diğer Yazılar

Google a Ekle

Add to Google



QueryString Güvenliği

Herkese merhaba

Web sayfalarımızın ziyaretçilerini (kullanıcılarını) bir sayfadan diğer sayfaya bir takım bilgiler ile beraber göndermek için kullandığımız yollardan birisi de url nin sonuna parametreler eklemektir. QueryString olarak adlandırılan bu yolda url nin sonunda ? karakteriyle başlayıp & karakteri ile ayrılan name-value tipindeki veri ile diğer sayfamızda gelen kişinin gerekli bilgilerini kolayca alabiliriz.

Örnek: http://www.dotnetgurus.net/QueryStringTestPage2?param1=1&param2=2

Bu url' deki sayfada yani QueryStringTestPage2.aspx sayfasında param1 ve param2 parametrelerinin değerlerini alarak gerekli yerlerde kullanabiliriz. Ancak bazı zamanlarda gerekli parametrelerin böyle açık açık kullanılmaması gerekir. Üyelik sistemi olan bir web uygulamasında, bir sayfada kullanıcının kim olduğunu ?UserId=yavuz şeklinde aldığınızı düşünsenize. Açık göz kullanıcıların yapacağı ilk iş sayfanın adresini ....?UserId=ahmet ...?UserId=mehmet şeklinde yazarak başka kullanıcıların gizli bilgilerine ulaşmaya çalışmak olacaktır. Durum daha az ya da daha çok ciddiyette olabilir ama ne olursa olsun querstring i güvenli olarak kullanmanız gereken zamanlar olacaktır.

Bu sorun öyle aman aman zor bir iş değil, biraz düşünerek herkes kendince bir yol bulabilir. Bu yazıda, ben de kendimce bulduğum bir yoldan bahsedeceğim. Belki birilerini bir kaç saatlik bir işten kurtarırım.

Koda bulaşmadan önce teorik olarak izleyeceğimiz yolu anlatayım.

- Öncelikle şifreleme işlemi yapabilen yani verilen metni şifreleyen ve verilen şifrelenmiş metni de geri açabilen bir fonksiyona ihtiyacımız var. Bunun için EncrytionUtility adındaki emektar class ımı kullanacağım. Şifreleme işlemini nasıl yaptığımızdan bahsetmeyeceğim ama kısaca belirteyim şifrelemeyi halk arasında AES olarak bilinen Rijndael algoritması ile yapıyoruz :)

- QueryString' i nasıl güvenli hale getireceğimize gelirsek. Bunun için QueryStringUtility adında bir class yazacağız. Bu class'ta temel olarak AddUrlParameter ve GetParameterValue adında 2 tane fonksiyonumuz olacak. Malum url oluşturma işlemi biraz gıcık bir iş hele hele işin içinde bir de querystring varsa sormayın. Url' de halen bir parametre var mı yok mu; varsa ? ile yoksa & ile başla vs vs.. dediğim gibi gıcık bir iş. Her seferinde bunlarla uğraşmayalım diye bu fonksiyonlar parametreleri şifrelemenin yanında bu işleri de bizim için yapsınlar dimi ama.

- Önemli bir diğer konu da neyi şifreleyeceğimiz. Yani sadece parametrenin değerini mi yoksa adıyla beraber tümünü mi şifrelyeceğiz. Ve bunun da ötesinde her parametre-değer ikilisini şifreledikten sonra bunları & ile mi birleştireceğiz yoksa tüm parametre-değer ikililerini & ile birleştirerek oluşan querystring i mi şifreleyeceğiz. Hangi yolu seçtiğimi ihtimalleri inceleyerek anlatmaya çalışayım.

http://www.abalaubala.com/UserInfo.aspx?userId=321&mode=3 şeklinde bir url ye ihtiyacımız olsun.

Sadece değerleri şifrelersek;

http://www.abalaubala.com/UserInfo.aspx?userId=skdflskdflsfl32owılkf&mode=qwyr7wbfjhdbjsa gibi bir urlmiz olur. Değerler değiştirilemez olsa da parametre adları ortada olduğu için benim çok hoşuma gitmedi, kötü kişilere arkadaki şeyler hakkında ipuçları veriyor. Ama yine de isteyen kullanabilir :)

Her parametre-değer ikilisini ayrı ayrı şifrelersek;

http://www.abalaubala.com/UserInfo.aspx?o3ruowıfoqroqwdflnf&kfhı8quwfqwx823rouwe gibi bir urlmiz olur. Bu ilkine göre daha hoş görünümlü :) ve bence daha güvenli bir yol. Ziyaretçi ne parametrenin değerini ne de parametrenin ne oldugunu görebiliyor. Ama kaç parametre olduğunu biliyor, tamam bu bir açık sayılmaz ama bir de aşağıdakine bakın, eminim sizin de içinizden gelen ses evet istediğim bu diyecektir :)

Tüm parametre-değer ikilileri toptan şirelenirse;

http://www.abalaubala.com/UserInfo.aspx?2e87y2ıfwkjdn8rwo23xuor8wodlıeruo8rwkfnkwxehrı8 gibi bir urlmiz olur. Nasıl ama süpppperrrrr dimi.

 

Şimdi ne istediğimizi bildiğimize göre bunu nasıl yapacağımızı görelim. Ekteki projeyi indirmeden nasılmış görelim diyenler için asıl işi yapan AddUrlParameter ve GetParameterValue fonksiyonlarını vererek lafı daha da uzatmayacağım malum kodu açıklamak zor bir iş ve bu yazıyı okuyorsanız bu yetenekte bir kişisiniz demektir :) Altı üstü çok da karışık olmayan string işlemleri var.

Kodu açıklamyacağım dedim ama dikkatinizi çekmek istediğim 2 nokta var. Şifreleme işleminden içinde neler olacağını bilemediğimiz türde karakterler olan garip bir metin çıkacağı ve bu metni url' de kullanacağımız için ve url' de her türlü karaktere izin verilmediği için AddUrlParameter fonksiyonu gerekli işlemleri yaptıktan sonra oluşan metni geri dönmeden önce frameworkün güzide fonksiyonlarından olan HttpUtility.UrlEncode fonksiyonu ile metni url' de kullanılacak bir hale getiriyor.

GetParameterValue fonksiyonu da frameworkün bir başka güzide fonksiyonu olan HttpUtility.ParseQueryString ile görevini yerine getiriyor.

İşte QueryStringSecuringUtility

Public Class QueryStringSecuringUtility 
     Public Shared Function AddUrlParameter(ByVal Url As String, ByVal ParameterName As String, ByVal ParameterValue As String) As String

          Dim myRawUrl As String = ""

          Dim myQstr As String = ""

          If Url.IndexOf("?") > 0 Then

               myRawUrl = Url.Split("?"c)(0)

               myQstr = Url.Split("?"c)(1)

               myQstr = EncrytionUtility.Decrypt(HttpUtility.UrlDecode(myQstr))

               myQstr &= "&"

          Else

               myRawUrl = Url

          End If

          myQstr += ParameterName.Trim() + "=" + ParameterValue.Trim() 
          Dim myUrl As String = myRawUrl + "?" + HttpUtility.UrlEncode(EncrytionUtility.Encrypt(myQstr))

          Return myUrl 
     End Function

     Public Shared Function DecryptUrl(ByVal SecuredUrl As String) As String

          Dim myUri As New Uri(SecuredUrl) 
          Dim myRawUrl As String = myUri.AbsoluteUri.Substring(0, myUri.AbsoluteUri.Length - myUri.Query.Length)

          Dim myQstr As String = myUri.Query.TrimStart("?"c) 
          Dim myUrl As String

          If myQstr.Length > 0 Then

               myUrl = myRawUrl + "?" + EncrytionUtility.Decrypt(HttpUtility.UrlDecode(myQstr)) 
          Else

               myUrl = myRawUrl

          End If

          Return myUrl 
     End Function

     Public Shared Function GetParameterValue(ByVal SecuredUrl As String, ByVal ParamName As String) As String

          Dim OpenUrl As String

          OpenUrl = DecryptUrl(SecuredUrl)

          If (String.IsNullOrEmpty(OpenUrl)) Then Return ""

          Dim OpenUri As New Uri(OpenUrl) 
          Return HttpUtility.ParseQueryString(OpenUri.Query).Get(ParamName)

     End Function 
End Class


Kullanımı

     Url Oluşturma

    Dim url As String = "http://www.abalaubala.com/UserInfo.aspx" 
    url = QueryStringSecuringUtility.AddUrlParameter(url, "userId", "432") 
    url = QueryStringSecuringUtility.AddUrlParameter(url, "mode", "3") 

    Parametreleri Alma

     Dim userId As String = QueryStringSecuringUtility.GetParameterValue(Request.Url.AbsoluteUri, "userId") 
    Dim mode As String = QueryStringSecuringUtility.GetParameterValue(Request.Url.AbsoluteUri, "mode") 

Önemli Son Bir Nokta 

Şimdi querystring' i güvene alma işini bu kadar inceledikten sonra son olarak açıkta kalan bir noktadan bahsetmek istiyorum. Yukarıda yapmış olduğumuz işlemlerde ziyaretçinin parametre değerlerini elle değiştirerek bizim istemediğimiz verileri görmesini engellemiş olduğumuzu düşünebilirsiniz. Maalesef bunu tam olarak başaramadık. Tamam belki url' yi elle değiştirerek bunu başaramaz zira böyle bir şey yaparsa geri açılamayacak bir metin oluşmuş olur ve parametreleri almaya çalıştığımızda exceptiona düşer. Peki şuna ne dersiniz aynı bilgisayarda aynı uygulamayı kullanan başka bir kullanıcı varsa ve kullanıcı login olduktan sonra internet tarayıcısının geçmişinden diğer kullanıcı için oluşmuş şifrelenmiş url' yi kopya-yapıştır ile kullanırsa ??? Maalesef bunu engelleyebilmiş değiliz. Çünkü kullanmış olduğumuz şifrelemede static bir key kullanıyoruz. Yani her kullanıcı ve her zaman için aynı querystring şifrelendiğinde hep aynı şifreli querystring oluşur.

Örnek : ?UserId=434 her zaman ?rı7wefuwqı8howrowrjor gibi bir şey verir. Ne zaman ve kim tarafından kullanılırsa kullanılsın.

Hayda 2 saattir ne diye uğraşıyoruz o zaman demeyin :) Bu küçük bir kaç yolla alt edilebilecek bir problem çünkü. Bu sorunu bir çok yolla aşabiliriz örnek vermek gerekirse;

- Şifreleme işleminde her kullanıcı için ayrı bir anahtar kullanabiliriz,

- QueryString'e SessionId bilgisini de ekleyerek ilgilisayfada QueryString' deki SessionId' si ile kullanıcını SessionId' si tutuyor mu tutmuyor mu diye kontrol edebiliriz,

- Kullanıcının Authenticate olmuş olması gereken bir sayfada kullanacaksak, hemen üst satırdaki çözüme benzer olarak SessionId yerine kullanıcı adı ya da Id sini kullanabiliriz,

- Güvenliği kullanıcı bazlı değil de zaman bazlı ele almak gereken bir durum ise QueryStringi oluştururken zamanı da işin içine katarak yönlendirilen sayfada bu zamanı kontrol edebilir. Örneğin QueryStringi oluştururken o andaki zamanı da parametre olarak ekleriz ve diğer sayfada gelen zaman o anki zamandan 1 dakikadan daha eskiyse parametreleri kabul etmeyebiliriz,

Görüldüğü üzere elimizde böyle bir araç olduktan sonra, ihtiyaca göre gerekli güvenlik önlemlerini almak hiç de zor değil.

Teşekkürler.

Bu yazıda anlatılan kodların yer aldığı örnek proje ekte:

QueryStringSecuring.zip (54,92 kb)


Categories: ASP.NET | VB.NET | Türkçe
Posted by yavuz on 26 Nisan 2009 Pazar 00:55
Permalink | Yorumlar (0) | Post RSSRSS comment feed