Skip to content

投稿

NHibernateリスナー(インターセプター)を使用した透過的暗号化の実装

2014年11月3日 • 7分で読める

NHibernateリスナー(インターセプター)を使用した透過的暗号化の実装

データベース内のデータを暗号化する必要に迫られたことはありますか?この記事では、nHibernateリスナーを使用して、データベースとの間でやり取りされるデータを暗号化・復号化する方法を探ります。暗号化はアプリケーションに対して透過的に行われます。

なぜこのようなことをしたいのでしょうか?SQL Serverには暗号化機能が製品に組み込まれています。それは確かですが、クラウドに移行してSQL Azureを使用したい場合は、何らかの暗号化戦略が必要になります。SQL Azureはデータベース暗号化をサポートしていません。

nHibernateリスナーとは何でしょうか?私はリスナーを、nHibernateの永続化とデータハイドレーションライフサイクルの特定の拡張ポイントに注入できるコードの一部として考えています。

この記事を書いている時点で、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

このリストは非常に豊富です。

透過的暗号化を実装するには、データを暗号化・復号化する適切な場所を見つける必要があります。データの暗号化にはIPostInsertEventListenerIPostUpdateEventListenerを使用します。これらのイベントで、データベースに送られる新しいデータと更新されたデータをキャッチします。復号化にはIPreLoadEventListenerを使用します。

このデモンストレーションでは、暗号化と復号化にDatabaseCryptographyクラスを使用します。暗号化の実装はこの記事では重要ではありません。

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

IPreUpdateEventListenerIPreInsertEventListenerの両方でfalseを返すことが重要です。そうしないと、insert/updateイベントが中止されてしまいます。

リスナーを実装したので、次にそれらをnHibernateに登録する必要があります。私はFluentNHibernateを使用しているので、生の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();
}

アプリケーションレベルでデータを復号化・暗号化すると、データベース内のデータが使用できなくなります。暗号化されたフィールドの値を読み取るには、データをアプリケーションに戻す必要があります。暗号化するフィールドを制限し、文字列値のみを暗号化したいと思います。文字列値以外を暗号化すると複雑になります。日付を暗号化できないということではありませんが、そうすると暗号化されたデータを保持するために、データベースの日付フィールドを文字列(nvarcharまたはvarchar)フィールドにする必要があり、これを行うとデータベースから日付フィールドを操作する能力を失います。

暗号化・復号化したいフィールドを識別するために、マーカー属性を使用します。

Encrypt属性

public class EncryptAttribute : Attribute
{
}

Decrypted属性

public class DecryptAttribute : Attribute
{
}

EncryptAttributeDecryptedAttributeの動作を確認するために、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)
        ;
    }

}

以上です。これで、データの暗号化と復号化がアプリケーションに対して透過的に行われるようになり、次のFacebookを構築することに専念できます。

著者:Chuck Conwayはソフトウェアエンジニアリングと生成AIを専門としています。ソーシャルメディアで彼とつながりましょう:X (@chuckconway) または YouTube をご覧ください。

↑ トップに戻る

こちらもおすすめ