Implementando Cifrado Transparente con Listeners (Interceptores) de NHibernate
3 de noviembre de 2014 • 5 min de lectura

¿Alguna vez has tenido que cifrar datos en la base de datos? En este post, exploraré cómo usar nHibernate Listeners para cifrar y descifrar datos que provienen y van hacia tu base de datos. La criptografía será transparente para tu aplicación.
¿Por qué querrías hacer esto? SQL Server tiene cifrado integrado en el producto. Eso es cierto, pero si te estás moviendo a la nube y quieres usar SQL Azure necesitarás algún tipo de estrategia de criptografía. SQL Azure no soporta cifrado de base de datos.
¿Qué es un Listener de nHibernate? Pienso en un Listener como un pedazo de código que puedo inyectar en puntos específicos de extensibilidad en el ciclo de vida de persistencia e hidratación de datos de nHibernate.
Al momento de escribir esto, los siguientes puntos de extensibilidad están disponibles en nHibernate.
IAutoFlushEventListener
IDeleteEventListener
IDirtyCheckEventListener
IEvictEventListener
IFlushEntityEventListener
IFlushEventListener
IInitializeCollectionEventListener
ILoadEventListener
ILockEventListener
IMergeEventListener
IPersistEventListener
IPostCollectionRecreateEventListener
IPostCollectionRemoveEventListener
IPostCollectionUpdateEventListener
IPostDeleteEventListener
IPostInsertEventListener
IPostLoadEventListener
IPostUpdateEventListener
IPreCollectionRecreateEventListener
IPreCollectionRemoveEventListener
IPreCollectionUpdateEventListener
IPreDeleteEventListener
IPreInsertEventListener
IPreLoadEventListener
IPreUpdateEventListener
IRefreshEventListener
IReplicateEventListener
ISaveOrUpdateEventListener
La lista es extensa.
Para implementar criptografía transparente, necesitamos encontrar el lugar correcto para cifrar y descifrar los datos. Para cifrar los datos usaremos IPostInsertEventListener
e IPostUpdateEventListener
. Con estos eventos capturaremos los datos nuevos y los datos actualizados que van hacia la base de datos. Para descifrar, usaremos el IPreLoadEventListener
.
Para esta demostración usaremos la clase DatabaseCryptography
para cifrar y descifrar. La implementación de criptografía no es importante para este artículo.
IPreLoadEventListener
public class PreLoadEventListener : IPreLoadEventListener
{
readonly DatabaseCryptography _crypto = new DatabaseCryptography();
///
/// Called when [pre load].
///
///The event. public void OnPreLoad(PreLoadEvent @event)
{
_crypto.DecryptProperty(@event.Entity, @event.Persister.PropertyNames, @event.State);
}
}
IPreInsertEventListener
public class PreInsertEventListener : IPreInsertEventListener
{
readonly DatabaseCryptography _crypto = new DatabaseCryptography();
///
/// Return true if the operation should be vetoed
///
///The event. /// true if XXXX, false otherwise.
public bool OnPreInsert(PreInsertEvent @event)
{
_crypto.EncryptProperties(@event.Entity, @event.State, @event.Persister.PropertyNames);
return false;
}
}
IPreUpdateEventListener
public class PreUpdateEventListener : IPreUpdateEventListener
{
readonly DatabaseCryptography _crypto = new DatabaseCryptography();
///
/// Return true if the operation should be vetoed
///
///The event. /// true if XXXX, false otherwise.
public bool OnPreUpdate(PreUpdateEvent @event)
{
_crypto.EncryptProperties(@event.Entity, @event.State, @event.Persister.PropertyNames);
return false;
}
}
Es importante notar que tanto IPreUpdateEventListener
como IPreInsertEventListener
deben retornar false, de lo contrario el evento de inserción/actualización será abortado.
Ahora que tenemos los Listeners implementados necesitamos registrarlos con nHibernate. Estoy usando FluentNHibernate
así que esto será diferente si estás usando nHibernate puro.
SessionFactory
public class SessionFactory
{
///
/// Creates the session factory.
///
/// ISessionFactory.
public static ISessionFactory CreateSessionFactory()
{
return Fluently.Configure()
.Database(MsSqlConfiguration.MsSql2012
.ConnectionString(c => c
.FromConnectionStringWithKey("DefaultConnection")))
.Mappings(m => m.FluentMappings.AddFromAssemblyOf())
.ExposeConfiguration(s =>
{
s.SetListener(ListenerType.PreUpdate, new PreUpdateEventListener());
s.SetListener(ListenerType.PreInsert, new PreInsertEventListener());
s.SetListener(ListenerType.PreLoad, new PreLoadEventListener());
})
.BuildConfiguration()
.BuildSessionFactory();
}
Cuando desciframos y ciframos datos a nivel de aplicación hace que los datos sean inútiles en la base de datos. Necesitarás traer los datos de vuelta a la aplicación para leer los valores de los campos cifrados. Queremos limitar los campos que son cifrados y solo queremos cifrar valores de cadena. Cifrar cualquier cosa que no sean valores de cadena complica las cosas. No hay nada que diga que no podemos cifrar fechas, pero hacerlo requerirá que el campo de fecha en la base de datos se convierta en un campo de cadena (nvarchar o varchar), para contener los datos cifrados, una vez que hagamos esto perdemos la capacidad de operar en el campo de fecha desde la base de datos.
Para identificar qué campos queremos cifrados y descifrados usaré atributos marcadores.
Atributo Encrypt
public class EncryptAttribute : Attribute
{
}
Atributo Decrypted
public class DecryptAttribute : Attribute
{
}
Para ver el EncryptAttribute
y el DecryptedAttribute
en acción echaremos un vistazo a la clase DatabaseCryptography
.
DatabaseCryptography
public class DatabaseCryptography
{
private readonly Crypto _crypto = ObjectFactory.GetInstance();
///
/// Encrypts the properties.
///
///The entity. ///The state. ///The property names.
public void EncryptProperties(object entity, object[] state, string[] propertyNames)
{
Crypt(entity, propertyNames, s = >
_crypto.Encrypt(s),
state)
;
}
///
/// Crypts the specified entity.
///
///
///The entity. ///The state. ///The property names. ///The crypt.
private void Crypt(object entity, string[] propertyNames, Func<string, string> crypt, object[] state) where T : Attribute
{
if (entity != null)
{
var properties = entity.GetType().GetProperties();
foreach (var info in properties)
{
var attributes = info.GetCustomAttributes(typeof (T), true);
if (attributes.Any())
{
var name = info.Name;
var count = 0;
foreach (var s in propertyNames)
{
if (string.Equals(s, name, StringComparison.InvariantCultureIgnoreCase))
{
var val = Convert.ToString(state[count]);
if (!string.IsNullOrEmpty(val))
{
val = crypt(val);
state[count] = val;
}
break;
}
count++;
}
}
}
}
}
///
/// Decrypts the property.
///
///The entity. ///The state. ///The property names.
public void DecryptProperies(object entity, string[] propertyNames, object[] state)
{
Crypt(entity, propertyNames, s = >
_crypto.Decrypt(s),
state)
;
}
}
Eso es todo. Ahora el cifrado y descifrado de datos será transparente para la aplicación y puedes continuar alegremente construyendo el próximo Facebook.
↑ Volver arribaTambién te puede gustar
- Modificar un Archivo Localmente Sin Actualizar el Repositorio Git Remoto 1 min de lectura
- Una Implementación de Búsqueda Binaria 1 min de lectura
- Los Beneficios de Usar un Framework de Construcción 2 min de lectura