ekrem özer

her yerde olan şeyler.

.Net Core Data Protection İle Veri Şifreleme

Merhaba arkadaşlar, bu makalemde .net core ile DataProtection kütüphanesini kullanarak veri şifrelemeyi anlatacağım. DataProtection ile istediğimiz bir metinsel ifadeyi yada byte dizisini encrypt/decrypt edebiliyoruz. 

Örnek olarak sitenize üye olanlardan e-mail doğrulaması istediğimizde maillerine gelen linklere tıkladıklarında parametre olarak bizim şifrelediğimiz mail adresi gelebilir ve biz o şifreli metni back-end kısmında çözüp mail doğrulaması yapabiliriz. Şimdi kodların nasıl olacağına birlikte bakalım.

Öncelikle Visual Studio ile .net core mvc projesi oluşturuyoruz. Solutionumun adına NetDataProtection web projemin adınada NetDataProtection.Web diyorum. Sonra kullanacağım statik parametreler için Core adında bir klasör oluşturup içine DataProtectorDefaults.cs adında bir class oluşturuyorum.

public static class DataProtectorDefaults
{
      public static string EmailValidation => "EmailValidation";
      public static int TimeHoursLimit => 24;
}

Makalenin konusundan sapmamak için işlemleri HomeControllerda yapacağım. Constructor metodumuzda IDataProtector nesnemizi tanımlıyorum ve controllerin üstüne doğrulama için kullanacağım email adreslerini manuel olarak ekliyorum. Sizin projenizde muuhtemelen veri database'den geliyor olacak.

 private readonly IDataProtector _dataProtector;
 private readonly List<string> _emailList = new List<string> { "ekrem@gmail.com", "hakan@gmail.com", "murat@gmail.com" };

 public HomeController(IDataProtectionProvider dataProtector)
 {
     _dataProtector = dataProtector.CreateProtector(DataProtectorDefaults.EmailValidation);
 }

Sonra kullanıcıdan email adresini alacağım basit bir form sayfası için action ve view ekliyorum.

Action

public IActionResult InputEmail()
{
    return View();
}

View.cshtml

@{
    ViewData["Title"] = "Input Email";
}
<h1>@ViewData["Title"]</h1>

@if (ViewBag.ValidationLink!=null)
{
    <p>
        Doğrulama Linkiniz: <a href="@ViewBag.ValidationLink">@ViewBag.ValidationLink</a>
    </p>
}

<form method="post">
    <label class="col-form-label">Email Adresiniz</label>
    <input type="text" name="email" class="form-control"/>
    <button>Gönder</button>
</form>

Sayfamın çıktısı şu şekilde oluyor;

Sonra bu sayfam içim post metodunu yazıyorum;

[HttpPost]
public IActionResult InputEmail(string email)
{
     var encryptedEmail = _dataProtector.Protect(email);
     var validationLink = $"/home/EmailValidation?email={encryptedEmail}";
     ViewBag.ValidationLink = validationLink;
     return View();
}

Bu metodda ilk satırda parametre olarak gelen email verisini şifreledim, ikinci satırdı kullanıcıya tıklaması için vereceğim linki oluşturdum, üçüncü satırda ise modelleme yapmadığım için oluşturduğum datayı kullanıcıya gösterebilmek için ViewBag'e attım. Kullanıcı inputa herhangi bir yazıp post ederse eğerse eğer (konumuzun dışında olduğu için inputa ek bir validasyon koymadım, şu haliyle mailden başka bir veride girebilir) sayfa post olduktan sonra aşağıdaki şekilde görünecek;


Gördüğünüz gibi parametre olarak gelen email adresi şifrelenmiş bir şekilde kullanıcıya gözüktü, şimdi linke tıklayıp gideceği kısmı yapalım, öncelikle EmailValidation action metodumuzu yazalım;

public IActionResult EmailValidation(string email)
{
     var decryptedEmail = _dataProtector.Unprotect(email);
     var isValid = _emailList.Contains(decryptedEmail);
     ViewBag.Message = isValid
                ? $"{decryptedEmail} email adresiniz doğrulandı."
                : $"{decryptedEmail} email adresiniz doğrulanamadı.";
     return View();
}

Bu metotda da ilk satırda parametre olarak gelen email datasını çözüyor, ikinci satırda ise controllerin üstünde oluşturduğumuz email listesinde çözdüğü emailin olup olmadığını kontrol ediyor, üçüncü satırda ise kullanıcıya döneceği mesajı oluşturuyor. Kullanıcı inputa ekrem@gmail.com yazıp sonra gelen linke tıklamış olursa aşağıdaki mesajı almış olacakır.

Görüldüğü gibi emaili çözümledi ve doğruladı, listemizde olmayan farkı bir email girdiğinde ise aşağıdaki mesajı alacaktır;

Kullanıcı listemizde olmayan bir email adresi girdiği için, şifre çözümlendi fakat doğrulanamadı.

Bu konuyla ilgili son olarakta şifrelediğimiz veriye bir ömür tanımlayabiliyoruz, yani bu email adresi bu link ile 24 saat içinde doğrulanmazsa linkin geçersiz olmasını sağlayabiliriz, onu yapmak içinde .ToTimeLimitedDataProtector(); metodundan faydalanıyoruz. InputEmail post metodumuzu şu şekilde güncellersek;

        [HttpPost]
        public IActionResult InputEmail(string email)
        {
            var timeLimited = _dataProtector.ToTimeLimitedDataProtector();
            var encryptedEmail = timeLimited.Protect(email, TimeSpan.FromHours(DataProtectorDefaults.TimeHoursLimit));
            //var encryptedEmail = _dataProtector.Protect(email);
            var validationLink = $"/home/EmailValidation?email={encryptedEmail}";
            ViewBag.ValidationLink = validationLink;
            return View();
        }

Metodda görüldüğü üzere timeLimited adında _dataProtector nesnesinden ToTimeLimitedDataProtector() metoduyla bir değişken türettim, ikinci satırda şifrelemeyi bu metodla yaptım, bu metodu kullanırken benden ilave olarak bir parametre daha istedi ve bu parametrede de DataProtectorDefaults sınıfımda tanımladığım TimeHoursLimit property'sini kullandım. Böylelikle 24 saat sonra linkime tıklansa şifre çözümlemesi yapılmayacaktır. Ben size sonucu göstermek için süreyi 5 saniyeye düşürdüm ve linke tıkladım, aldığım hata ise;

Gördüğünüz gibi şifrelemeyi çözmedi ve süre aşımı hatası verdi, tabi burada kendi projenize göre exceptionu yönetmeniz gerekiyor. Exceptionu yakalamak için aşağıdaki gibi bir try catch kod bloğu kullanabiliriz;

        public IActionResult EmailValidation(string email)
        {
            try
            {
                var decryptedEmail = _dataProtector.Unprotect(email);
                var isValid = _emailList.Contains(decryptedEmail);
                ViewBag.Message = isValid
                    ? $"{decryptedEmail} email adresiniz doğrulandı."
                    : $"{decryptedEmail} email adresiniz doğrulanamadı.";
            }
            catch (CryptographicException ex)
            {
                ViewBag.Message = $"Hata oluştu: {ex.Message}";
            }
            return View();
        }

Exception bloğunun aldığı parametrenin tipini CryptographicException olarak değiştirmemiz hatayı daha sağlıklı yakalamamızı sağlayacaktır. Bu haliyle süresi dolmuş bir linke tıkladığımızda kullanıcı aşağıdaki responsu alacaktır.

Bir sonraki makalede görüşmek üzere, umarım faydalı olmuştur.
Projenin kaynak kodları: https://github.com/ekremozer/NetDataProtection