一部の情報をデータベースに暗号化して保存する必要があります (ハッシュ化されていません。ここではパスワードについては説明していません) が、特定のシナリオを検証するためにその情報をメモリ内で読み取ることができます (したがって、ハッシュ化ではなく暗号化が必要です)。
将来変更される可能性があるため、どの列を暗号化してデータベースに格納する必要があるかを簡単に特定できる方法が必要です。そうは言っても、次のアプローチが、MVC5 および Entity Framework 6 の IdentityProvider によって提供される ApplicationDbContext で機能するかどうか疑問に思っていました。注意すべき注意事項はありますか? それとも、これはアイデアへの良いアプローチですか?そうでない場合は、ガイダンスをいただければ幸いです。
- 列を定義する (コード ファースト) ために、Code-First モデルのさまざまなプロパティに適用できる "StoreSecurelyAttribute" という属性を作成しました。
[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field)]
public class StoreSecurelyAttribute : Attribute { }
- 次に、この属性を安全に格納する必要がある任意の列に適用できます (現在サポートされているのは文字列のみです)。
public class UserProfileInfo
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public Guid Id { get; set; }
[Required, StoreSecurely]
public string SomePersonalInformation { get; set; }
}
- 次に、ApplicationDbContext コンストラクターに以下を追加します。
public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
{
public ApplicationDbContext() : base("DefaultConnection", false)
{
var ctx = ((IObjectContextAdapter) this).ObjectContext;
ctx.ObjectMaterialized += OnObjectMaterialized;
ctx.SavingChanges += OnSavingChanges;
}
void OnSavingChanges(object sender, EventArgs e)
{
foreach (var entry in ((ObjectContext) sender).ObjectStateManager.GetObjectStateEntries(EntityState.Added |
EntityState.Modified))
{
foreach (var propInfo in entry.Entity.GetType().GetProperties()
.Where(prop => prop.PropertyType == typeof(string) && Attribute.IsDefined(prop, typeof(StoreSecurelyAttribute))))
{
var plainTextValue = propInfo.GetValue(entry.Entity) as string;
// TODO: encrypt using injected encryption provider
var encryptedValue = Encrypt(plainTextValue);
propInfo.SetValue(entry.Entity, encryptedValue);
}
}
}
void OnObjectMaterialized(object sender, ObjectMaterializedEventArgs e)
{
foreach (var propInfo in e.Entity.GetType().GetProperties()
.Where(prop => prop.PropertyType == typeof(string) && Attribute.IsDefined(prop, typeof(StoreSecurelyAttribute))))
{
var encryptedValue = propInfo.GetValue(e.Entity) as string;
// TODO: decrypt using injected encryption provider
var plainTextValue = Decrypt(encryptedValue);
propInfo.SetValue(e.Entity, plainTextValue);
}
}
public override int SaveChanges()
{
// Hold onto them before their state changes and they're no longer "added" or "modified"
var entries = ChangeTracker.Entries().Where(x => x.State == EntityState.Added || x.State == EntityState.Modified).ToList();
// Go Ahead and save...
var result = base.SaveChanges();
// After saving to db, we want our local hydrated object to be "correct" so... decrypt...
foreach (var entry in entries)
{
foreach (var propInfo in entry.Entity.GetType().GetProperties()
.Where(prop => prop.PropertyType == typeof(string) && Attribute.IsDefined(prop, typeof(StoreSecurelyAttribute))))
{
var encryptedValue = propInfo.GetValue(entry.Entity) as string;
var plainTextValue = Decrypt(encryptedValue);
propInfo.SetValue(entry.Entity, plainTextValue);
}
}
return result;
}
// ... snip ...
}
更新:これは多くのリフレクションであり、遅くなる可能性があることはわかっていますが、フィールドの暗号化と復号化に夢中になることはなく、暗号化するフィールドは「すべてのリクエストで」ヒットするわけではありません.