DPAPI ile string şifrelemesi ve SecureString class

Bir uygulama geliştirirken göz önünde bulundurmamız gereken önemli konulardan biri de hiç şüphesiz “güvenlik”tir. Güvenlik konusu deyince nedense aklımıza daha çok network tabanlı bir koruma gelir. Oysa birçok saldırının kaynağı uygulama içerisindeki açıklardan kaynaklanmaktadır. Bir uygulamayı güvenli kılmanın birçok yöntemi bulunmaktadır. Hakların düzenlenmesi, doğru ayarların yapılması, kullanıcıdan gelen bilgilerin doğrulanması, etkili validation işlemlerinin yapılması, gizli-kritik bilgilerin uygulama içerisinde saklanmaması, etkin hata yönetiminin sağlanması, aktif bir test gerçekleştirilmesi, veritabanının koruma altına alınması, .NET platformunda unmanaged kaynaklarının doğru yönetilmesi bu yöntemlerden sadece bir kaçı. Bu yazıda uygulama güvenliğini sağlayan yöntemlerden biri olan ve Windows işletim sisteminin 2000 ve sonraki sürümleri tarafından desteklenen DPAPI (Data Protection Application Programming Interface) arabiriminin .NET cephesi hakkında konuşacağız.
Data koruma aracı olan Data Protection API , Triple-DES algoritmasını kullanarak verileri kişi ve makine bazlı encrypt ve decrypt işlemini sağlar. Bu API’nin çalışma mantığı ve işlevi ile ilgili ayrıntılı bilgiyi MSDN ‘de bulabilirsiniz.DPAPI, temelde CryptProtectData ve CryptUnprotectData fonksiyonlarına sahiptir. .NET teknolojisi, güvenlikle ilgili uygulama ve sistem bazlı System.Security altında bulunan birçok sınıf içermektedir. Bu kütüphane altındaki nesneleri verileri şifrelerken DPAPI birimini kullanır. Daha önceki versiyonlarda tipi API çağırma mantığıyla bu işlem yapılırdı. Fakat 2.0 ile birlikte doğrudan System.Security.Cryptography altında bulunan ProtectedData sınıfı bu arabirimini wrap etmiştir. ProtectedData classı, Protect() ve Unprotect() isminde iki static fonskiyon içermektedir. public static byte[ ] Protect(byte[ ] userData, byte[ ] optionalEntropy, System.Security.Cryptography.DataProtectionScope scope)
public static byte[ ] Unprotect(byte[ ] encryptedData, byte[ ] optionalEntropy, System.Security.Cryptography.DataProtectionScope scope)
Uygulamalarımızda Data Protection API için wrapper yazmak yerine .NET 2.0’in ProtectedData sınıfını kullanmamız daha kolay olacaktır. Uygulamalarda en çok şifrelenen bilgilerden biri Connection String bilgisidir. Uygulamamızın beslendiği veritabanının güvenlik bilgilerinin bulunduğu bu cümleyi şifreli halde saklamamız başkası tarafından ilk bakışta okunması engellemiş olur. Aşağıdaki örnekte Server=AKAYMAZ\AKAYMAZ;Database=Csharp;uid=sa;pwd=sa olarak tutulan connection string değeri, encrypt ve decrypt edilmiştir. Protect() ve Unprotect() metodlarında değerleri kullanıcı veya makine bazında şifreleyip çözebildiğimiz gibi her kullanıcını bu değeri çözmemesi için de bir anahtar(entropy) oluşturulur. Şifrelenmiş değeri çözerken kullanıcıdan bu anahtar istenilebilir.

using System;
using System.Security.Cryptography;
using System.Text;

class Program
{
    static void Main()
    {
        string EntropyStr = "AhmetKaymaz";
        string CnnStr = @"Server=AKAYMAZ\AKAYMAZ;Database=DB;uid=;pwd=";

        //Stringleri byte dizilere çevirelim
        byte[] EntropyArr = Encoding.Unicode.GetBytes(EntropyStr);
        byte[] CnnArr = Encoding.Unicode.GetBytes(CnnStr);

        //Bilgiyi encrypt edelim
        byte[] EncryptedArr = ProtectedData.Protect(CnnArr, EntropyArr, DataProtectionScope.CurrentUser);
        //Byte türündeki bu diziyi ekrana yazdıralım.
        //PrintBytes(EncryptedArr);

        //Encode edilmiş datayı decrypt edelim
        byte[] DecryptedArr = ProtectedData.Unprotect(EncryptedArr, EntropyArr, DataProtectionScope.CurrentUser);
        Console.WriteLine(Encoding.Unicode.GetString(DecryptedArr));
        Console.ReadLine();
    }//Main

    //Byte türündeki diziyi yazdırma metodu
    static void PrintBytes(byte[] bytes)
    {
        foreach (byte b in bytes)
        {
            Console.Write("{0:x2}, ", b);
        }
    }//PrintBytes

}//Program
Imports System
Imports System.Security.Cryptography
Imports System.Text

Module Module1
    Sub Main()
        'GetPassword();
        Dim EntropyStr As String = "AhmetKaymaz"
        Dim CnnStr As String = "Server=AKAYMAZ\AKAYMAZ;Database=DB;uid=;pwd="

        'Stringleri byte dizilere çevirelim
        Dim EntropyArr As Byte() = Encoding.Unicode.GetBytes(EntropyStr)
        Dim CnnArr As Byte() = Encoding.Unicode.GetBytes(CnnStr)

        'Bilgiyi encrypt edelim
        Dim EncryptedArr As Byte() = ProtectedData.Protect(CnnArr, EntropyArr, DataProtectionScope.CurrentUser)
        'Byte türündeki bu diziyi ekrana yazdıralım.
        PrintBytes(EncryptedArr)

        'Encode edilmiş datayı decrypt edelim
        Dim DecryptedArr As Byte() = ProtectedData.Unprotect(EncryptedArr, EntropyArr, DataProtectionScope.CurrentUser)
        Console.WriteLine(Encoding.Unicode.GetString(DecryptedArr))
        Console.ReadLine()
    End Sub

    'Byte türündeki diziyi yazdırma metodu
    Private Sub PrintBytes(ByVal bytes As Byte())
        For Each b As Byte In bytes
            Console.Write("{0:x2}, ", b)
        Next
    End Sub

End Module

System.Security.SecureString Class
.NET 2.0 ile birlikte gelmiş ve uygulama güvenliğini sağlayan önemli classlardan biri de SecureString sınıfıdır. Bilindiği gibi bazı değerleri anlık olarak tutmak için değişkenleri kullanırız. Değişkenler de memory üzerinde tutulur. CLR, Garbage Collector mekanizmasını kullanarak memory üzerinde tutulan bu değerlerin yerlerini değiştirebilir bu konum değişimi sırasında değişkenlerin hafıza üzerinde birden fazla kopyası oluşabilir. Ayrıca bu değerler, memory üzerinde encrypt edilmedikleri için bir başkası uygulamamızın çalıştığı process’e ait memory alanını tarayıp bu bilgiye ulaşabilir. Ayrıca string veri türünün, “immutable” karakterinde olması da bir güvenlik açığıydı.. Yani string değişkeninin değerini değiştirmek istediğimizde doğrudan eski değer üzerine yazmak yerine yeni konumlu bir değişken tanımlar. Bu sorunları çözmek için .NET 2.0, etkileyici bir özellik olan SecureString sınıfını sunmaktadır. Bu sınıf türünde tanımladığımız değerler DPAPI ( Data Protection API) tarafından memory üzerinde encrypt edilmiş olarak saklanır. Bu durumda değişkenin, memory üzerindeki konumu değişmez(pinned in memory) ve hiç bir zaman memory üzerinde birden fazla kopyası bulunmaz. Connection string veya kredi kartı gibi bilgileri bu sınıf türünde tanımlayabiliriz. Değeri memoryye yüklerken bu sınıfın char türünde değer alan AppendChar() metodu kullanılır. SecureString türünde memory üzerinde tutulan değeri okumak için System.Runtime.InteropServices.Marshal.SecureStringToBSTR() metodu kullanılır.

using System;
using System.Security;
using System.Runtime.InteropServices;
//. . . ..
SecureString SecStr = new SecureString();
//Değeri AHMET olan şifreyi hafızaya atalım
SecStr.AppendChar('A');
SecStr.AppendChar('H');
SecStr.AppendChar('M');
SecStr.AppendChar('E');
SecStr.AppendChar('T');

IntPtr Ptr = Marshal.SecureStringToBSTR(SecStr);
string Str = Marshal.PtrToStringUni(Ptr);
Console.WriteLine(Str);
Dim SecStr As New SecureString()
'Değeri AHMET olan şifreyi hafızaya atalım
SecStr.AppendChar("A")
SecStr.AppendChar("H")
SecStr.AppendChar("M")
SecStr.AppendChar("E")
SecStr.AppendChar("T")

Dim Ptr As IntPtr = Marshal.SecureStringToBSTR(SecStr)
Dim Str As String = Marshal.PtrToStringUni(ptr)
Console.WriteLine(Str) 

Bir yanıt yazın

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