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 を返す必要があることに注意することが重要です。そうしないと、挿入/更新イベントが中止されます。

リスナーを実装したので、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
{
}

Decrypt 属性

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 を構築する道を進むことができます。

Author: Chuck Conway is an AI Engineer with nearly 30 years of software engineering experience. He builds practical AI systems—content pipelines, infrastructure agents, and tools that solve real problems—and shares what he’s learning along the way. Connect with him on social media: X (@chuckconway) or visit him on YouTube and on SubStack.

著者: Chuck Conwayは、ソフトウェアエンジニアリングの経験が30年近くあるAIエンジニアです。彼は実用的なAIシステム(コンテンツパイプライン、インフラストラクチャエージェント、実際の問題を解決するツール)を構築し、学んだことを共有しています。ソーシャルメディアで彼とつながってください: X (@chuckconway) または YouTubeSubStack で彼を訪問してください。

↑ トップに戻る

こちらもおすすめ