Skip to content

Publicaciones

Implementar Cifrado Transparente con Oyentes de NHibernate (Interceptores)

3 de noviembre de 2014 • 5 min de lectura

Implementar Cifrado Transparente con Oyentes de NHibernate (Interceptores)

¿Alguna vez ha tenido que cifrar datos en la base de datos? En este artículo, exploraré cómo usar nHibernate Oyentes para cifrar y descifrar datos que van hacia y vienen de su base de datos. La criptografía será transparente para su aplicación.

¿Por qué querría hacer esto? SQL Server tiene cifrado integrado en el producto. Eso es cierto, pero si se está moviendo a la nube y desea usar SQL Azure, necesitará algún tipo de estrategia de criptografía. SQL Azure no admite cifrado de base de datos.

¿Qué es un Oyente de nHibernate? Pienso en un Oyente como un fragmento de código que puedo inyectar en puntos de extensibilidad específicos en el ciclo de vida de persistencia e hidratación de datos de nHibernate.

En el 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 actualizados que van a la base de datos. Para descifrar, usaremos 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 devolver false, de lo contrario el evento de inserción/actualización será abortado.

Ahora que tenemos los Oyentes implementados, necesitamos registrarlos con nHibernate. Estoy usando FluentNHibernate así que esto será diferente si está usando nHibernate sin procesar.

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 se descifran y cifran datos a nivel de aplicación, los datos se vuelven inútiles en la base de datos. Necesitará traer los datos de vuelta a la aplicación para leer los valores de los campos cifrados. Queremos limitar los campos que se cifran y solo queremos cifrar valores de cadena. Cifrar cualquier cosa que no sea 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 hacemos 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.

Encrypt Attribute

public class EncryptAttribute : Attribute
{
}

Decrypted Attribute

public class DecryptAttribute : Attribute
{
}

Para ver EncryptAttribute y 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 puede continuar construyendo el próximo Facebook.

Autor: Chuck Conway es un Ingeniero de IA con casi 30 años de experiencia en ingeniería de software. Construye sistemas de IA prácticos—canalizaciones de contenido, agentes de infraestructura y herramientas que resuelven problemas reales—y comparte lo que está aprendiendo en el camino. Conéctate con él en redes sociales: X (@chuckconway) o visítalo en YouTube y en SubStack.

↑ Volver arriba

También te puede interesar