使用EF6和身份提供程序安全地存储列数据

时间:2014-04-04 18:34:50

标签: ef-code-first entity-framework-6 asp.net-identity owin asp.net-mvc-5.1

我要求将一些信息存储在数据库中加密(不是哈希,我不在这里讨论密码),但仍然能够在内存中读取该信息以验证某些情况(因此加密) ,而不是哈希)。

我想要一种干净简单的方法来识别哪些列应该在数据库中以加密方式存储,因为它们将来可能会发生变化。话虽如此,我想知道以下方法是否适用于由MVC5和Entity Framework 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 ...
}

更新:我知道这是很多反思,可能会很慢 - 但我们不会在加密和解密字段时过于疯狂,而且我们加密的字段也不会受到影响。在每个请求上。&#34;

1 个答案:

答案 0 :(得分:0)

我会故意对受保护数据进行读/写操作。

另外,我不知道你是否需要从它所写的相同位置读取数据,但如果没有,则考虑使用非对称加密来保护数据。这样,数据写入的位置(例如Web应用程序)只需要访问私钥,而系统中的其他位置只需要访问公钥即可访问公钥。