The Protector: NetCore #S1B1

Cihat Solak
lTunes Tribe
Published in
6 min readApr 21, 2021
AspNetCore Security — Güvenlik Önlemleri

Netflix’in yeni sezon dizilerinden herhangi biri değil. Üzerine konuşacağımız muhafızın İstanbul’u korumak gibi bir amacı da yok fakat projeyi kötü niyetli/saldırgan kişilerden korumak 🛡️ gibi özel bir görevi var. Tılsımlı gömlek yerine “secret.json”, Hançer yerine “IDataProtector”, Yüzük yerine de Cors yapılandırmalarımız var. Bu bölümde AspNetCore projelerimizde güvenliği sağlamanın birkaç farklı yolundan bahsedeceğiz. #S1B2 çok yakında.

Veri Koruması — Data Protection👮‍♂️👮‍♀️

NetCore uygulamalarımızda örneğin web projesi geliştirdiğimiz sırada bazı önemli verilerimizi dış dünyaya açmak istemeyebilir ya da gizlemek isteyebiliriz. Örneğin telefon numarası, e-posta gibi önemli bilgilerin üzerinden sorgulama yaptığımızda bu bilgilerin url üzerinde görüntülenmesini istemeyebiliriz. (Belki de url üzerinde hiçbir verinin görüntülenmesini istemiyorsun?)

Açıkçası kullanıcıyı ilgilendiren, dış dünyada olmaması gereken verilerle işlem yapmak istiyorsan ve bu işlemleri yaparken de dış dünyaya açmak istemiyorsan, Reçeteye 📝 IDataProtector, ITimeLimitedDataProtector interfacelerini yazıyorum ihtiyaç duydukça kullanabilirsin.

NetCore uygulamaları gömülü (dahili) bir şekilde DataProtection özelliğini bizlere sağlıyor. Bu açıdan pratikte url üzerinde taşınan verinin güvenliğini sağlayabilir, gizlenmek istenen verileri şifreleyip 🔐 daha sonra bu şifreleri çözümleyebiliriz. Hali hazırda bu yapıyı kullanmak için ayrı bir katman ya da middleware(ara uygulama/yazılım) yazmaya ihtiyaç yoktur. Bahsi geçen interfaceleri kullanmak istediğimiz yerde DI(Dependency Injection) olarak kurucuya(constructor) ekliyoruz ve daha sonra (IDataProtector, ITimeLimitedDataProtector ) interface’inin Protect(), Unprotect() isimli metotlarıyla birlikte şifreleme ve şifreme çözümleme işlemleri gerçekleştiriyoruz.

IDataProtector: Şifreleme işlemini veriye ömür vermeden yapar.

ITimeLimitedDataProtector(Zaman Sınırlı Veri Koruyucu): Şifreleme işlemlerinde şifrelediğimiz veriye bir ömür vermek ⏰ istersek. (Örneğin: bu veriyi şifrele ama 10 saniye içinde çözülmezse o veri çözülemesin gibi.)

  • Protect() : Koru : Şifreleme
  • Unprotect() : Korumayı Kaldır : Şifre Çözücü

Nasıl Kullanırım? 🧐

Startup.cs içerisinde ConfigureServices içerisinde eklememizi yapıyoruz.

Startup.cs

Örnek bir ProductController oluşturup üzerinden konuşalım.

ProductController

IDataProtectionProvider interfacesinin CreateProtector metodu ile bir protector oluşturulmaktadır. CreateProtector içerisinde verdiğimiz isim benzersiz (unique)’dir ve DataProtector’ları birbirinden ayırmak (izole etmek) için kullanırız. Farklı bir controller içerisinde de DataProtector kullanabileceğimizden ötürü bunları birbirinden ayırmak 🔗 mahiyetinde isimlendirme yapıyoruz. Amacımız DataProtector kullanan farklı bir uygulama tarafından şifrelenmiş verinin çözümlenmesini engellemektir.

MVC — {controller}/{action}/[id}

TimeLimitedDataProtector güvenliği bir üst seviyeye çıkarmak için şifrelemelere süre verebilme imkânı sağlar. Örneğin bir şifreleme yaptığımızda o şifrenin 15 saniye ile çözülebileceğini yani 15 saniye geçtikten sonra şifrenin deşifre işlemi gerçekleştirilemeyeceğini belirtiyor olacağız. Bu yapı WEB API tarafındaki token mantığına benzemektedir.

Şifreleme işlemini hangi isim ile yaptıysan, çözümleme işleminde de aynı isme sahip protector ile yapmalısın‼️

IDataProtector — Query String

Yukarıdaki resimde HTTP GET isteklerinin tamamını QueryStringMiddleware (içerik sonundaki github adresinden incelenebilir) ile otomatik olarak şifreleyip action tarafında bu şifrelerin çözümlü halde modellerime ulaşmasını sağlıyorum. Buradan da anlaşılacağı üzere route şeması haricinde QueryStringleride tamamıyla şifreleyebiliriz.

AK Koyun & Kara Koyun — IP Kontrolü 🚧

Uygulamanızda güvenliği artırmak amacıyla sadece belirli ip adreslerinden gelen isteklere cevap vermek istiyorsunuz ya da tam tersi durumda da belirlediğiniz ip adreslerinden gelmeyene cevap vermek istemiyorsunuz. Bu gibi durumlarda eğer karmaşık bir yapınız yoksa ve basit birkaç kısıtlama koymak istiyorsanız uygulama seviyesinde (middleware) ya da ActionFilter ile controller/action seviyesinde ip control işleminin işinizi göreceğini düşünüyorum. Daha karmaşık bir yapıya sahipseniz, örneğin dışa tamamen açık(herhangi bir doğrulaması olmayan) servis adreslerine sahipseniz “AspNetCoreRateLimit” Nuget paketini incelemenizi öneririm.

AspNetCoreLimit

Örnek olması amacıyla appsettings.json dosyasında belirlemiş olduğum ip adresleri haricinde gelen istekleri kabul etmeyeceğim bir middleware oluşturdum.

IPSafeMiddleware — WhiteList & BlackList

Middleware’ı startup.cs içerisinde eklemeyi unutmayalım. 🤯

IPSafeMiddleware — Configure

Ip kontrolü IIS, Load Balancer, Nginx seviyesinde de konumlandırılabilir.

Controller/Action olarak spesifik bir ip kontrolü için IActionFilter interfacesinden miras alacağımız class içerisinde 2 adet implements edeceğimiz metot bulanacaktır. (OnActionExecuting, OnActionExecuted)

Ip Kontrolü — 403 Forbidden

OnActionExecuting metodu controllerda yazmış olduğumuz action’a gitmeden uğrayacağı son duraktır. Bu kısım içerisinde ip kontrollerimizi yapabiliriz. Bu işlemlerden sonra hazırladığımız ActionFilter’ı tanımlamak istediğimiz controller veya action’a attribute olarak tanımlamamız yeterlidir.

Gizli Yönetici — Secret Manager Tools 🕵️

Projeniz içerisinde önem arz eden bilgileriniz kesinlikle vardır. Bunlar ConnectionStrings, Basic Auth, herhangi bir kullanıcı adı, şifre veya Thirdy Party Id’ler olabilir. Bu gibi verileri appsettings.json içerisinde tutmak yerine User Secrets da tutmamız daha sağlıklı olacaktır.

Solutin Explorer — Manage User Secrets

Projeyi development ortamda çalıştırırsak secret.json içerisindeki değerler runtime da appsettings.json ile birleştiriliyor.

secret.json dosyası uygulamanın dosya ağacında değil, tamamen projenin yapısından ayrı bir yerde C:\Users\{username}\AppData\Roaming\Microsoft\UserSecrets dizininde (şifrelenmiş olarak değil)plain text olarak tutuluyor.

Bu şekilde örneğin github, gitlab vb. kullandığımızda daha geniş bir ifadeyle public repository’e publish attığımızda secret.json içerisindeki veriler publish edilemeyecektir.

Genel itibarıyla uygulamamızda kritik 👨‍💻 önem arz eden, güvenliği sağlanması gereken verileri secret manager tool’un sağlamış olduğu secret.json dosyası ile uygulamadan ayırabilir böylece appsettings.json dosyasına erişilse dahi kritik verilerimiz kötü niyetli kişiler tarafından erişimine kesin bir engel koyulmuş olunacaktır.

Production ortamda secret.json dosyasına erişilememektedir. Çözüm olarak Environment değişkenler kullanılmalıdır. Özetle, Production ortamına özgün enviroment değişken tanımlarıyla güvenliğimizi sağlayabilir, Development ortamda ise secret.json’u kullanabiliriz.

Kökler Arası Kaynak Paylaşımı — Cors 📬

Bir web sayfasından farklı bir domaine bir request işlemi gerçekleştirildiği zaman tarayıcılar farklı domaine yapılan istekleri güvenlik amacıyla reddederler. Örneğin ajax isteği gerçekleştirdiğimde sadece kendi sitemizdeki kaynaklara erişim yetkimiz vardır. Farklı uygulamadaki kaynağa erişebilmek için uygulamanın cors ayarlamalarının yapılması gerekmektedir.

Some-Origin Policy, zararlı sitelerin diğer sitelerden hassas bilgileri okumasını da engellemektir. Kullanıcılar için Session/Cookie çok önemli güvenlik önlemini alıyor.

Tarayıcılar için farklı domain demek protocol, host ya da portun farklı olması demektedir. Http ile Https birbirinden farklı domaindir ya da site.com:2785 ile site.com:5405 de farklı bir domaindir. Bu nedenle origin belirlerken origin adresimizi doğru belirlesek dahi yeterli olmayıp port numarasını da doğru konfigüre etmemiz gerekmektedir.

Access-Control-Allow-Origin

Web uygulaması bir request(istek) yaptığı zaman, eğer API uygulamasından response olarak header’da “NO Access-Control-Allow-Origin” görürse some-origin policy burada devreye giriyor ve yapılan isteği red ediyor.

Cors, tarayıcılardan gelen some-origin policy güvenliğini esnetmek amacıyla geliyor. Buradan da anlaşılacağı üzere cors güvenlik önlemi değil, tarayıcıların default olarak gelen some-origin policy güvenliğini hafifletmek amacıyla kullanılıyor.

Cors’dan kasıt alsın da bu Web API uygulaması hangi domaindan gelen istekleri kabul edecek? Sorunun cevabını karşılıyor. Örneğin api uygulamasını “https://ltunes.tribe.com" adresinden gelen istekleri karşılayacağım şekilde tarayıacılara bilgi verirsem bu durumda some-origin policy güvenlik önlemini uygulamayacaktır.

Tarayıcı isteğin olumlu/olumsuz olduğunu nereden anlıyor? 🤔

Başarısız ise gelen requeste karşılık responseda No ‘Access-control-allowed-origin’ algılayıp, some-origin policy’nin devreye girmesiyle.

Başarılıda ise web uygulaması, Web API ye istek yaptığında requestinde origin=”key” isimli bir değer bulundurur. Bu key originin kaynağıdır. Örn: “Origin: https://ltunes.tribe.com" Eğer API uygulamasında cors özelliği aktifse ve “https://ltunes.tribe.com" adresinden gelen istekleri kabul edilecek bir yapılandırma yapıldıysa, response da hangi domainlere izin verdiğini belirten domain adresi gönderiyor. Tarayıcıda requestteki origin ile response da ki Access-Control-Allow-Origin değerlerini karşılaştırarak eğer birebir aynısı ise bu yapılan isteğe cevap veriyor. Burada same-origin policy’i esnetmiş oluyoruz.

Birkaç örnek cors tanımlamasını inceleyebiliriz.

ConfigureServices

[EnableCors(“PolicyName”)] ve [DisableCors] attributeleri ile cors ayarlarımızı controller veya action seviyesinde de yapabiliriz.

Son olarak cors özelliği AspNetCore uygulamalarına özel bir durum değil, örneğin NodeJS ya da Java uygulamasıda geliştiyorsanız, cors özelliğini açıp, gerekli konfigürasyonları ihtiyaca göre belirlemelisiniz.

Tot ziens 😎

--

--