我需要将一些信息存储在加密的数据库中(不是散列,我在这里不是在谈论密码),但仍然能够在内存中读取该信息以验证某些场景(因此是加密,而不是散列)。
我想要一种简洁的方法来确定哪些列应该在数据库中加密存储,因为它们将来可能会改变。话虽如此,我想知道以下方法是否可以与 MVC5 和实体框架 6 中的 IdentityProvider 提供的 ApplicationDbContext 一起使用。有什么需要注意的警告吗?或者这甚至是一个很好的想法?如果没有,任何指导将不胜感激。
- 为了定义列(代码优先),我创建了一个名为“StoreSecurelyAttribute”的属性,该属性可以应用于 Code-First 模型中的不同属性。
[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 ...
}
更新:我知道这是很多反思,可能会很慢——但我们不会对加密和解密字段过于疯狂,我们加密的字段不会“在每个请求上”都被命中。