Skip to content

Innlegg

Implementering av transparent kryptering med NHibernate Listeners (Interceptors)

3. november 2014 • 5 min lesing

Implementering av transparent kryptering med NHibernate Listeners (Interceptors)

Har du noen gang måttet kryptere data i databasen? I dette innlegget vil jeg utforske hvordan man bruker nHibernate Listeners for å kryptere og dekryptere data som kommer fra og går inn i databasen din. Kryptografien vil være transparent for applikasjonen din.

Hvorfor ville du gjøre dette? SQL Server har kryptering innebygd i produktet. Det er sant, men hvis du flytter til skyen og ønsker å bruke SQL Azure trenger du en slags kryptografistrategi. SQL Azure støtter ikke databasekryptering.

Hva er en nHibernate Listener? Jeg tenker på en Listener som et stykke kode som jeg kan injisere inn i spesifikke utvidelsespunkter i nHibernate persistering og datahydrering livssyklus.

Per denne skrivingen er følgende utvidelsespunkter tilgjengelige i 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

Listen er omfattende.

For å implementere transparent kryptografi må vi finne det rette stedet å kryptere og dekryptere dataene. For å kryptere dataene vil vi bruke IPostInsertEventListener og IPostUpdateEventListener. Med disse hendelsene vil vi fange opp nye data og oppdaterte data som går inn i databasen. For dekryptering vil vi bruke IPreLoadEventListener.

For denne demonstrasjonen vil vi bruke DatabaseCryptography klassen for kryptering og dekryptering. Kryptografiimplementeringen er ikke viktig for denne artikkelen.

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;
}
}

Det er viktig å merke seg at både IPreUpdateEventListener og IPreInsertEventListener må returnere false, ellers vil insert/update hendelsen bli avbrutt.

Nå som vi har implementert Listeners må vi registrere dem med nHibernate. Jeg bruker FluentNHibernate så dette vil være annerledes hvis du bruker rå nHibernate.

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();
}

Når man dekrypterer og krypterer data på applikasjonsnivå gjør det dataene ubrukelige i databasen. Du må bringe dataene tilbake inn i applikasjonen for å lese verdiene til de krypterte feltene. Vi ønsker å begrense feltene som krypteres og vi ønsker bare å kryptere strengverdier. Å kryptere noe annet enn strengverdier kompliserer ting. Det er ingenting som sier at vi ikke kan kryptere datoer, men å gjøre det vil kreve at datofelt i databasen blir et streng(nvarchar eller varchar) felt, for å holde de krypterte dataene, når vi gjør dette mister vi muligheten til å operere på datofeltet fra databasen.

For å identifisere hvilke felt vi ønsker kryptert og dekryptert vil jeg bruke markørattributter.

Encrypt Attribute

public class EncryptAttribute : Attribute
{
}

Decrypted Attribute

public class DecryptAttribute : Attribute
{
}

For å se EncryptAttribute og DecryptedAttribute i aksjon vil vi ta en titt inn i DatabaseCryptography klassen.

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)
        ;
    }

}

Det var det. Nå vil kryptering og dekryptering av data være transparent for applikasjonen og du kan fortsette på din muntre vei med å bygge neste Facebook.

Forfatter: Chuck Conway spesialiserer seg på programvareutvikling og Generativ AI. Koble til ham på sosiale medier: X (@chuckconway) eller besøk ham på YouTube.

↑ Tilbake til toppen

Du liker kanskje også