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 du bruker nHibernate Listeners til å 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 vil bruke SQL Azure, trenger du en eller annen krypteringsstrategi. 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 persistens- og datahydrasjonslivssyklusen.

Fra og med 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 riktig sted å kryptere og dekryptere dataene. For kryptering av dataene bruker vi IPostInsertEventListener og IPostUpdateEventListener. Med disse hendelsene vil vi fange opp nye data og oppdaterte data som går inn i databasen. For dekryptering bruker vi IPreLoadEventListener.

For denne demonstrasjonen bruker vi 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 du dekrypterer og krypterer data på applikasjonsnivå, gjør det dataene ubrukelige i databasen. Du må bringe dataene tilbake til applikasjonen for å lese verdiene til de krypterte feltene. Vi ønsker å begrense feltene som er kryptert, og vi ønsker bare å kryptere strengverdier. Kryptering av 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 string(nvarchar eller varchar) felt for å holde de krypterte dataene. Når vi gjør dette, mister vi muligheten til å operere på datofelt fra databasen.

For å identifisere hvilke felt vi ønsker kryptert og dekryptert, bruker jeg markeringsattributter.

Encrypt Attribute

public class EncryptAttribute : Attribute
{
}

Decrypted Attribute

public class DecryptAttribute : Attribute
{
}

For å se EncryptAttribute og DecryptedAttribute i aksjon, tar vi et blikk 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 er 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 er en AI-ingeniør med nesten 30 års erfaring innen programvareutvikling. Han bygger praktiske AI-systemer—innholdspipelines, infrastrukturagenter og verktøy som løser virkelige problemer—og deler det han lærer underveis. Koble til ham på sosiale medier: X (@chuckconway) eller besøk ham på YouTube og på SubStack.

↑ Tilbake til toppen

Du kan også like