Derinlemesine yazılım eğitimleri için kanalımı takip edebilirsiniz...

Asp.NET Core – Data Protection İle Veri Koruma

Merhaba,

Bir uygulamanın hukuki boyutundan beşeri boyutuna kadar tüm detaylarını ilgilendiren ve uygulamanın kalitesini ortaya koyarak, tercih edilebilirlik oranını belirleyen en büyük etkenlerden birinin veri güvenliği olduğunu düşünebiliriz. Günlük hayattan edindiğimiz tecrübelerde göstermektedir ki, hayatımıza pratik anlamda çok fazla katkıda bulunabilen onlarca basit ve etkili uygulamaların arasında verisel anlamda sızıntı şüphesi barındıranlar, sahalarına uygun gelişimlerini ve yeterlilik seviyelerini olabildiğince olumsuz izlenime maruz bıraktıklarından dolayı kullanıcılar tarafından tercih edilmemektedirler ve çok kısa zamanda maziye karışabilmektedirler. Dolayısıyla bu derece hayati bir konu olan veri güvenliği üzerine Asp.NET Core uygulamaları çerçevesinde istişare edecek ve operasyonel olarak pratikte nasıl bir tutum sergilenmesi gerektiğini konuşacağız.

Veri Güvenliği Nedir?

Veri güvenliği bütünsel açıdan değerlendirildiğinde, uygulamaya ve hatta uygulamayı kullanan kitlelere dair verilerin mümkün mertebe irademiz dışında dış dünyaya sızmasını/sızdırılmasını engellemek yahut olabildiğince minimize edebilmek için meşakkatli bir tefekkür ve operasyon gerektiren bir sürecin neticesidir diyebiliriz.

Veri Güvenliği Neden Önemsenmelidir?

Özellikle uygulamayı tercih eden kullanıcılar tarafından karşılıklı güven ilişkisine dayanarak paylaşılan verilerle birlikte, uygulamaya dair kritik arz edebilecek verilerin dış dünya tarafından erişilebilmesi büyük zaafiyetler doğurabileceği gibi büyük bir mahcubiyetinde tezahürü olabilir. O yüzden veri güvenliği kesinlikle ötelenmemeli ve özellikle kitlelere hitap edecek bir uygulama projesinde prensip olarak sıralamada öncelik olarak belirlenmelidir.

Veri Koruması(Data Protection) Nedir?

Uygulamaya dair veri güvenliğinin sağlanabilmesi için yapılması gereken aktivitelerden sadece bir tanesidir. Uygulama yahut uygulamayı kullanan kullanıcılara dair kritik verilerin genellikle url üzerinde taşınması durumunda gizlenmesi bir başka deyişle şifrelenmesidir. Bu duruma istinaden aşağıdaki url örneklerini inceleyebilirsiniz;

/Details/5
/Details/CfDJ8NJCCNrydG5OoLjy-PIjlIX2rVMPEsXyZ9rvhQJdJDxXRr5zyt_hiDhRRlBmtLo1npprgm2CMnQRcBWylcVWq8fjvwyngsfad
x45l83ol1YsBBQyQ5IVuKrZxJadhKnsqw

Asp.NET Core Uygulamalarında Veri Koruması

Asp.NET Core uygulamalarında veri koruması açısından url üzerinde taşınan veri güvenliğini sağlayabilmek için dahili olarak gelen DataProtection servisiyle birlikte IDataProtector interface’inin kullanabiliriz.

Bunun için öncelikle uygulamanın ‘Startup.cs’ dosyasındaki ‘ConfigureServices’ metodunda aşağıdaki gibi ‘AddDataProtection’ servisinin eklenmesi gerekmektedir.

    public class Startup
    {
        .
        .
        .
        public void ConfigureServices(IServiceCollection services)
        {
            .
            .
            .
            services.AddDataProtection();
            .
            .
            .
        }
        .
        .
        .
    }

Ardından istenilen controller’da ‘IDataProtectionProvider’ arayüzü ile ‘DataProtection’ isteğinde bulunulabilir.

    public class EmployeeController : Controller
    {
        IDataProtector _dataProtector;
        public EmployeeController(IDataProtectionProvider dataProtectionProvider)
        {
            _dataProtector = dataProtectionProvider.CreateProtector("protectorname1");
        }
    }

Burada ‘IDataProtectionProvider’ arayüzünün ‘CreateProtector’ metodu ile ismi parametrede belirtildiği gibi olan bir protector oluşturulmaktadır. Bu protector ile url üzerinde taşınacak olan veri şifrelenecektir. Bu şekilde inşa edilmesinin sebebi, bir uygulamada birden fazla protectorı birbirinden izole bir şekilde oluşturabilmek içindir.

Örneğin aşağıdaki yapılanmayı incelerseniz eğer;

    public class EmployeeController : Controller
    {
        IDataProtector _dataProtector1;
        IDataProtector _dataProtector2;
        public EmployeeController(IDataProtectionProvider dataProtectionProvider)
        {
            _dataProtector1 = dataProtectionProvider.CreateProtector("protectorname1");
            _dataProtector2 = dataProtectionProvider.CreateProtector("protectorname2");
        }
    }

birbirinden izole edilmiş ‘_dataProtector1’ ve ‘_dataProtector2’ isminde birinin şifresini bir diğerinin çözümleyemeyeceği şekilde iki protector oluşturulmuştur. Bu yapılanmanın esas amacı, DataProtector ile şifrelenen verilerin yine DataProtector kullanan başka bir uygulama tarafından çözümlenmesini engellemek içindir. Bundan dolayı burada yazılan metinsel değerler uygulama için secret özellik göstermektedirler.

Velhasıl… Şimdi bu servisi nasıl kullanacağımıza odaklanalım.

Genellikle url üzerindeki veriler bir entity’nin id kolonundan gelen veriler olabilmektedirler. O yüzden bizler örneklendirme için veri koruma senaryosu olarak personellerin listesi üzerinden konuyu seyredeceğiz.

    public class Employee
    {
        public int Id { get; set; }
        public string Name { get; set; }
        public string Email { get; set; }

        [NotMapped]
        public string EncryptedId { get; set; }
    }

Yukarıdaki kod bloğuna göz atarsanız bir personeli modellediğimiz ‘Employee’ class’ı oluşturulmuştur. Yapısal olarak içerisinde olguya dair memberlar tanımlandığı gibi ayriyetten ‘Id’ kolonundaki verinin şifrelenmiş versiyonunu temsil edecek olan ‘EncryptedId’ propertyside tanımlanmıştır. Şimdi! burada çok kritik arz edebilecek olan bir hususa dikkatinizi çekmek istiyorum. Gerçek hayatta örnekteki ‘Employee’ gibi entityler genellikle Entity Framework Core gibi ORM araçlarıyla kullanılan sınıflar olacağından dolayı Database First yöntemleri ile otomatik oluşturulan sınıflar olabilirler. Bu otomatik süreçte entity sınıflar yeniden oluşturulacağından ve mevcut olanlar ezileceğinden dolayı sizlerin/bizlerin yapmış olduğu değişiklikler kalıcı olarak silinecektirler. Böyle bir durumda hiçbir zaman entity classlarda ekstradan eklenecek olan elemanlar direkt ilgili sınıfa fiziksel olarak eklenmezler.
Soru : Peki nereye ekleyeceğiz hocam?
Cevap : İlgili sınıfın partial classına…

    public partial class Employee
    {
        public int Id { get; set; }
        public string Name { get; set; }
        public string Email { get; set; }
    }
    public partial class Employee
    {
        [NotMapped]
        public string EncryptedId { get; set; }
    }

İşte doğru yaklaşım neticesinde doğru bir tasarım… Bundan sonra yapılacak herhangi bir değişiklikte ‘Employee’ sınıfı yeniden oluşturulabilir lakin partial’ı ezilmeyeceği ve korunacağı için her değişiklik neticesinde ‘EncryptedId’ propertysi içerisine dahil edilmiş olacaktır.

Ayrıca ilgili property’nin ‘NotMapped’ attribute’u ile işaretlenmesinin sebebi ORM araçlarında karşılığı olmayan bir kolon olduğunu bildirmek içindir. Aksi taktirde hata ile karşılaşma olasılığınız yüksek ihtimaldir.

Şimdi ilgili sınıf türünden nesneler barındıran bir koleksiyonu view’e gönderen aşağıdaki action metodu inceleyelim.

    public class EmployeeController : Controller
    {
        IDataProtector _dataProtector;
        public EmployeeController(IDataProtectionProvider dataProtectionProvider)
        {
            _dataProtector = dataProtectionProvider.CreateProtector("protectorname1");
        }

        List<Employee> employees = new List<Employee>
            {
                new Employee{ Id = 1, Name = "Hilmi", Email = "hilmi@gmail.com" },
                new Employee{ Id = 2, Name = "Hüseyin", Email = "huseyin@gmail.com" },
                new Employee{ Id = 3, Name = "Şükrü", Email = "sukru@gmail.com" },
                new Employee{ Id = 4, Name = "Şuayip", Email = "suayip@gmail.com" },
                new Employee{ Id = 5, Name = "Rıfkı", Email = "rifki@gmail.com" },
                new Employee{ Id = 6, Name = "Mülayim", Email = "mulayim@gmail.com" }
            };

        public IActionResult Index()
        {
            employees.ForEach(emp => emp.EncryptedId = _dataProtector.Protect(emp.Id.ToString()));

            return View(employees);
        }
    }

Burada ’employees’ koleksiyonunun veritabanından geldiğini varsayabilirsiniz. Asıl dikkat etmeniz gereken nokta 21. satırdaki işlemdir. Burada tüm nesnelerin ‘Id’ kolonları ‘DataProtector’ nesnesinin ‘Protect’ metoduyla şifrelenerek üretilen değer ‘EncryptedId’ kolonuna atanmaktadır.

Aşağıdaki kod bloğuna bakarsanız eğer employee’ların detay linklerinde ‘Id’ değerleri değil, şifrelenmiş ‘EncryptedId’ değerleri listelenmektedir.

@model IEnumerable<Employee>

<table class="table">
    <thead>
        <tr>
            <th>Name</th>
            <th>Email</th>
            <th></th>
        </tr>
    </thead>
    <tbody>
        @foreach (var employee in Model)
        {
            <tr>
                <td>@employee.Name</td>
                <td>@employee.Email</td>
                <td>
                    <a asp-action="Details" asp-route-id="@employee.EncryptedId">Detay</a>
                </td>
            </tr>
        }
    </tbody>
</table>

Asp.NET Core - Data Protection İle Veri Koruma

Böylece uygulamada kritik arz eden verilerimizi korumaya almış bulunmaktayız.

Bu adımdan sonra sırada, herhangi bir employee üzerinden detay isteğinde bulunulursa şifrelenmiş ‘EncryptedId’ değerini tekrar çözümlenmesi ve ilgili employee’a yönlendirme yapılması vardır. Bunun içinde aşağıdaki çalışmayı inceleyiniz;

        public IActionResult Details(string id)
        {
            Employee employee = employees.FirstOrDefault(emp => emp.Id == int.Parse(_dataProtector.Unprotect(id)));
            return View(employee);
        }

Görüldüğü üzere ‘id’ parametresinden gelen şifrelenmiş değeri ‘Unprotect’ metoduyla çözerek ilgili nesneyi elde etmekte ve view’e göndermekteyiz.

ToTimeLimitedDataProtector İle Şifrelenmiş Veriye Ömür Biçme

‘ITimeLimitedDataProtector’ arayüzü ile şifrelenen verilerin çözümlenme süresine bir ömür biçebiliriz.

    public class EmployeeController : Controller
    {
        ITimeLimitedDataProtector _dataProtector;
        public EmployeeController(IDataProtectionProvider dataProtectionProvider)
        {
            _dataProtector = dataProtectionProvider.CreateProtector("protectorname1").ToTimeLimitedDataProtector();
        }
    }

Kullanımı;

            _dataProtector.Protect(emp.Id.ToString(), TimeSpan.FromMinutes(1))

Belirtilen süre zarfında şifrelenmiş veri çözülebilecek aksi taktirde çözülemeyecektir. Böylece ihtiyaç doğrultusunda geçici urller oluşturabilir ve kullanıcı zamansal açıdan sınırlandırılabilir.

İlgilenenlerin faydalanması dileğiyle…
Sonraki yazılarımda görüşmek üzere…
İyi çalışmalar…

Bunlar da hoşunuza gidebilir...

2 Cevaplar

  1. ramazan dedi ki:

    Süpersiniz

  2. Burhan dedi ki:

    Merhaba, Söylediğiniz gibi veri detay da çalışıyor ancak veri güncellerken
    public IActionResult Update(string id)
    metodunu kullanırken Update Get te çalışıyor ancak
    Update Post talebinde yapıldığı zaman Model içindeki Id 0(Sıfır) geliyor.
    Update Get metoduna veri gönderirken Id=1 olarak gidiyor.

Bir yanıt yazın

E-posta adresiniz yayınlanmayacak. Gerekli alanlar * ile işaretlenmişlerdir