3 Nisan 2017 Pazartesi

ASP.NET, Güvenli Veritabanı Bağlantısı(Connection String Encrypt)

ASP.NET’de veri tabanlarına bağlantı kurarken connection string ifadesinin açık olarak, runtime da yada web.config de yazılması, yazılım güvenliği açısından risk oluşturacağından connection string ifadesinin açık olarak yazılması istenmeyen bir durumdur. Bu yüzden connection string ifadesinin şifrelenmesi gerekir. Aşağıda, şifreleme işlemi için iki yöntem önereceğim;
            Web.config dosyasında Encrypt/Decrypt Yöntemi : Bu yöntemde, Microsoft’un bize sunmuş olduğu kriptolama Tool ‘unu anlatmaya çalışacağım. Bu Tool , “aspnet_regiis.exe” dir. Eğer bilgisayarınızda .net framework 4 kurulu ise, “C:\WINDOWS\Microsoft.NET\Framework\v4.0.30319” gibi bir path içersine komut yorumlayıcısı(cmd) aracılığı ile girerseniz , bu dosyayı görebilirsiniz. Kullanım için, cmd ile ilgili path e girdikten sonra aşağıdaki kullanım komutlarını yazarak web.config içerisindeki “connectionStrings” attribute unun şifrelenmesi yada şifresinin çözülmesi işlemlerini yapabilirsiniz.
Generic olarak kullanımı;
a)aspnet_regiis -pe "connectionStrings" -app "/SampleApplication" (SampleApplication pathindeki web.config içindeki connectionStrings attribute unu kriptolar)
b)aspnet_regiis -pe "connectionStrings" -app "/SampleApplication" -prov "RsaProtectedConfigurationProvider" (SampleApplication pathindeki web.config içindeki connectionStrings attribute unu specific provider kullanarak kriptolar)
c)aspnet_regiis -pd "connectionStrings" -app "/SampleApplication" (SampleApplication pathindeki web.config içindeki connectionStrings attribute unun kriptosunu çözer.)
           Şifreleme işlemini yaptıktan sonra, publish yapacağınız her ortam daki veri tabanı connection stringi değişiyorsa, ilgili ortamlar için connection string encrypt işlemini yapıp, sonra da web.config transformlarını yazmanız gerekmektedir!
            Ayrıca şifreleme için default “RsaProtectedConfigurationProvider” kripto kütüphanesi kullanmadan custom RSA kripto kütüphanesi yazarsanız kendi kurumunuz  yada şirketiniz adına size özel bir şifreleme yapmış olursunuz.Yazılım güvenlik seviyesini biraz daha artırmak istiyorsanız Custom RSA Provider yöntemini kullanabilirsiniz.
            Örneğin, encryption öncesi web.config connection string değeri;
  <connectionStrings>
    <add name="DefaultConnection" connectionString="Data Source=XXXXXXX;Initial Catalog=XXXXXXX;integrated Security=true;"
      providerName="System.Data.SqlClient" />
  </connectionStrings>
            Encryption sonrası web.config connection string değeri aşağıdakine benzer olur ve aşağıdaki örnekteki gibi transformu yazılabilir;
  DB Connection string transform;
  <connectionStrings configProtectionProvider="XXXXXX"  xdt:Transform="Replace">
    <EncryptedData Type="http://www.w3.org/2001/04/xmlenc#Element"
      xmlns="http://www.w3.org/2001/04/xmlenc#">
      <EncryptionMethod Algorithm="http://www.w3.org/2001/04/xmlenc#tripledes-cbc" />
      <KeyInfo xmlns="http://www.w3.org/2000/09/xmldsig#">
        <EncryptedKey xmlns="http://www.w3.org/2001/04/xmlenc#">
          <EncryptionMethod Algorithm="http://www.w3.org/2001/04/xmlenc#rsa-1_5" />
          <KeyInfo xmlns="http://www.w3.org/2000/09/xmldsig#">
            <KeyName>Rsa Key</KeyName>
          </KeyInfo>
          <CipherData>
            <CipherValue>hM3bWWJDSmoP+LNSnRgVbYxoA4Hk8wBtiGuIsTYSCTjob1G46JHuwbWqA3AugHWQF8gLrSdlHCYwUJBv6PCUheE1bfUxVkehAgrQdOjJc/0ehoma09GP/uTFCSaojZbdqIgSO0wI8B5ZR18N/Zp9sFxHWFD+RK+XwJ5nGwhjIWY=</CipherValue>
          </CipherData>
        </EncryptedKey>
      </KeyInfo>
      <CipherData>
        <CipherValue>7WXzMYCceaTmpTQeeOX3EVDGely1kS0zmt3YsigERLXG6L5sAkm9rzSm9zetM0S08A4gdpFH4i+kfyKkUOG356nf+GRODD1mhkBLmFXGIqKeTdUrqdaeJvOBVRSk4hjJf1YSC5ZTXGMKZweCmswbgf1fQz+i8aaXH9Ci6dhKxE466oyos/vCuxayvc8Hgfl6N8P6HrCWZDqBcSWLoZybK5dVsKN+oX1knPHkyY7rO6vQrGDwtN2qORd3D/+NgadXMqlezmTVy+IEh/OlNdeU1reOx4/ZzjHb++RIM+clyVIGaM+zcZ5iNQ8skkJi62fENm+5+1fw8Z/cOzvK8d7mMgvYIYX0PUbcsnNEYRSRzg8Vtb4ble0gBHD40ULei4Q6</CipherValue>
      </CipherData>
    </EncryptedData>
</connectionStrings>

Windows Registry aracılığıyla Encrypt/Decrypt Yöntemi : Bu yöntemde, connection string değeri runtime da registry den decrypt edilerek okunur. Tabiki connection string ifadesinin daha önceden registry de encrypt edilmiş halinin veritabanından veya uygulama sunucularından sorumlu kişi tarafından kayıt edilmiş olması gerekmektedir.
Bu yöntemi, yazılım geliştirme, veri tabanı ve sistem departmanlarının sorumlulukları birbirinden bağımsız olarak tasarlanmış projelerde daha güvenli ve daha esnek yöntem olarak kullanabilirsiniz. Projelerinizin genelinde kullandığınız Encrypt ve Decrypt kütüphanelerini kullanarak connection string in şifrelenmesi ve şifresinin çözülmesi işlemlerini istediğiniz güvenlik düzeyine göre esnek olarak yapabilme imkanınız olacaktır. Böylece veri tabanı connection sorumluluğunu her deployment ortamındaki sorumlu kişiye, development ortamından bağımsız ve esnek olarak vermiş olursunuz. Tabiki, veri tabanı connection undan sorumlu kişilere Registry ye connection string ifadesinin şifrelenmiş halini kayıt edebilmeleri için, projelerinizde kullanmış olduğunuz Encrypt/Decrypt uygulamasını vermeniz zorunlu olacaktır. Bu şekilde, veritabanı connection undan sorumlu kişi veri tabanı connection string i değiştiği zaman registry den ilgili connection string value sunu değiştirerek sorumluluğu tamamen üzerine almış olacaktır.
Aşağıda, ASP.NET Identity Framework DB Connection string inin runtime da Windows Registry ‘den decrypt edilerek okunması örneği yapılmıştır;
Registry e yazacağımız Connection string path ini Web.config içerisinde ki <configration> attribute unun altına aşağıdaki şekilde tanımlıyoruz;
  <appSettings>
    <add key="ConnectionStringRegistryKey" value="MuhtarConnStr" />   
  </appSettings>
  <appDataConfiguration>
    <ConnectionStringRegistryKeyPath value="SOFTWARE\ConnStr" />
  </appDataConfiguration>

Windows Registry’ içerisine aşağıdaki key/value değerlerini kayıt ediyoruz;
Registry Key name: "SOFTWARE\ConnStr” path inin içinde “MuhtarConnStr"
Registry Local DB Decrypted Value : "Data Source=XXXXXXX;Initial Catalog=XXXXXXX;integrated Security=true;"
Registry Local DB Encrypted Value : AAANA9ppHNzmLnkMrFQMwreKWcBFSTwc+LEB4bj9mnF9/pPwxupTkFjVh1472rxcCDPsda1Bt0tD2SGv8enHDobHghSErXy0MxhQfZEqjHnLjNv5Zn/aidLTW4cuaVSVgdls4x7Xg097Yz4QuL1nd7DUcfo=

Sonra, Model classında, Identity Framework connection alma işleminin yapıldığı “ApplicationDbContext” classının içerisinde, runtime da connection alma işleminin yapıldığı bölümde windows registry den “MuhtarConnStr” connection string değerini aşağıdaki gibi alıp decrypt ederek, DB connection işleminin güvenli ve sorunsuz olarak yapılabilmesini sağlıyoruz;
namespace TalepBasvuru.Models
{
    [Serializable]
public class ApplicationUser : IdentityUser { }
public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
    {
        public ApplicationDbContext() : base()
        {
            var dataConfigurationSettings = EngineContext.Current.Resolve<IDataConfigurationSettings>();
            string registryPath = dataConfigurationSettings.ConnectionStringRegistryKeyPath;
            var connectionStringRegistryKey = ConfigurationManager.AppSettings["ConnectionStringRegistryKey"];
            string connStr = TalepBasvuruHelper.GetConnectionStringFromRegistry(registryPath, connectionStringRegistryKey);
            if (string.IsNullOrEmpty(connStr)) {
                StringBuilder errorMessage = new StringBuilder("Registry de '");
                errorMessage.Append(registryPath).Append(@"\").Append(connectionStringRegistryKey).Append("' keywordu bulunamadi. Lütfen veritabani baglantisi icin connection string tanımlamasini yapin!");
                throw new NullReferenceException(errorMessage.ToString());
            }
            else
                base.Database.Connection.ConnectionString = connStr;
        }
    }
}

Helper classında ki yardımcı methodlar;
public static ICryptography Cryptography { get { return Context.Current.Resolve<ICryptography>(); } }

public static string GetConnectionStringFromRegistry(string pRegistryPath, string pConnectionStringRegistryKey)
        {
            string connStr = null;
            IDictionary<string, object> registryListItems = GetValues(pRegistryPath);
            if (registryListItems.ContainsKey(pConnectionStringRegistryKey))
            {
                string encryptedConnStr = registryListItems[pConnectionStringRegistryKey] as string;
                connStr = Cryptography.Decrypt(encryptedConnStr);
            }
            return connStr;
        }

private static Dictionary<string, object> GetValues(string pRegistryPath)
{
            var values = new Dictionary<string, object>();
            RegistryKey root = RegistryKey.OpenBaseKey(RegistryHive.LocalMachine,RegistryView.Registry64);
            root = root.OpenSubKey(pRegistryPath);
            if (root != null)
            {
                foreach (var value in root.GetValueNames())
                {
                    try
                    {
                        values.Add(string.Format(value), (root.GetValue(value).ToString() ?? ""));
                    }
                    catch (Exception)
                    {
                       
                    }
                }
                return values;
            }
            return null;
}