gpt4 book ai didi

c# - 使用 ASP.NET Core Entity Framework 在数据层中进行数据加密

转载 作者:行者123 更新时间:2023-11-30 22:56:08 27 4
gpt4 key购买 nike

我目前正在设计一个需要加密存储数据的 Web 应用程序。
计划使用的技术:

  • ASP.NET 核心 API
  • ASP.NET Core Entity Framework
  • MS SQL Server 2012
  • 任何 Web 前端
  • 由于规范,我们需要将所有数据加密存储在数据库中。

  • 这将是实现这一目标的好方法,同时仍然能够使用 Entity Framework 和 LINQ,因此开发人员不必处理加密。
    是否可以加密整个数据库?

    最佳答案

    首先,不要混淆 encrypting with hashing ,在 Eastrall 的回答中,它们暗示您可以对密码字段使用加密。 不要这样做

    此外,每次加密新值时都应该更改初始化向量,这意味着您应该避免像 Eastrall 的库那样为整个数据库设置单个 IV 的实现。

    现代加密算法的设计速度很慢,因此加密数据库中的所有内容至少会略微影响您的性能。

    如果处理得当,您的加密负载将不仅仅是密文,还应包含加密 key 的 ID、所用算法的详细信息和签名。这意味着与等效的纯文本相比,您的数据将占用更多空间。如果您想了解如何自己实现,请查看 https://github.com/blowdart/AspNetCoreIdentityEncryption。 (该项目中的自述文件无论如何都值得一读)

    考虑到这一点,您项目的最佳解决方案可能取决于将这些成本最小化对您来说有多重要。

    如果您要像 Eastrall 的答案中的库一样使用 .NET Core Aes.Create();,则密文将是 byte[] 类型。您可以将数据库提供程序中的列类型用于 byte[] ,或者您可以编码为 base64 并存储为 string 。通常存储为字符串是值得的:base64 将比 byte[] 多占用大约 33% 的空间,但更容易使用。

    我建议使用 ASP.NET Core Data Protection stack 而不是直接使用 Aes 类,因为它可以帮助您进行 key 轮换并为您处理 base64 中的编码。您可以使用 services.AddDataProtection() 将其安装到您的 DI 容器中,然后让您的服务依赖于 IDataProtectionProvider ,可以像这样使用:

    // Make sure you read the docs for ASP.NET Core Data Protection!

    // protect
    var payload = dataProtectionProvider
    .CreateProtector("<your purpose string here>")
    .Protect(plainText);

    // unprotect
    var plainText = dataProtectionProvider
    .CreateProtector("<your purpose string here>")
    .Unprotect(payload);

    当然, read the documentation 不要只是复制上面的代码。

    ASP.NET Core Identity, the IdentityUserContext 中,使用值转换器对标有 [ProtectedPersonalData] 属性的个人数据进行加密。
    Eastrall 的图书馆也在使用 ValueConverter

    这种方法很方便,因为它不需要您在实体中编写代码来处理转换,如果您遵循领域驱动设计方法(例如 .NET Architecture Seedwork ),这可能不是一种选择。

    但是有一个缺点。如果您的实体上有很多 protected 字段。下面的代码将导致 user 对象上的每个加密字段都被解密,即使没有一个被读取。

    var user = await context.Users.FirstOrDefaultAsync(u => u.Id == id);
    user.EmailVerified = true;
    await context.SaveChangesAsync();

    您可以通过在您的属性上使用 getter 和 setter 来避免使用值转换器,如下面的代码。但是,这意味着您需要在实体中放置特定于加密的代码,并且您必须连接到任何加密提供程序的访问权限。这可能是一个 static 类,或者您必须以某种方式传递它。

    private string secret;

    public string Secret {
    get => SomeAccessibleEncryptionObject.Decrypt(secret);
    set => secret = SomeAccessibleEncryptionObject.Encrypt(value);
    }

    然后,您每次访问该属性时都会进行解密,这可能会在其他地方导致您意想不到的麻烦。例如,如果 emailsToCompare 非常大,下面的代码可能会非常昂贵。

    foreach (var email in emailsToCompare) {
    if(email == user.Email) {
    // do something...
    }
    }

    您可以看到您需要在实体本身或提供程序中记住您的加密和解密调用。

    避免使用值转换器同时仍然对实体外部或数据库配置隐藏加密是复杂的。因此,如果性能是一个很大的问题,以至于您无法使用值转换器,那么您的加密可能不是您可以从应用程序的其余部分隐藏的东西,您可能希望运行 Protect()Unprotect() 调用完全在 Entity Framework 代码之外的代码。

    这是一个示例实现,其灵感来自 ASP.NET Core Identity 中的值转换器设置,但使用 IDataProtectionProvider 而不是 IPersonalDataProtector :

    public class ApplicationUser
    {
    // other fields...

    [Protected]
    public string Email { get; set; }
    }

    public class ProtectedAttribute : Attribute
    {
    }

    public class ApplicationDbContext : DbContext
    {
    public ApplicationDbContext(DbContextOptions options)
    : base(options)
    {
    }

    public DbSet<ApplicationUser> Users { get; set; }

    protected override void OnModelCreating(ModelBuilder builder)
    {
    // other setup here..
    builder.Entity<ApplicationUser>(b =>
    {
    this.AddProtecedDataConverters(b);
    });
    }

    private void AddProtecedDataConverters<TEntity>(EntityTypeBuilder<TEntity> b)
    where TEntity : class
    {
    var protectedProps = typeof(TEntity).GetProperties()
    .Where(prop => Attribute.IsDefined(prop, typeof(ProtectedAttribute)));

    foreach (var p in protectedProps)
    {
    if (p.PropertyType != typeof(string))
    {
    // You could throw a NotSupportedException here if you only care about strings
    var converterType = typeof(ProtectedDataConverter<>)
    .MakeGenericType(p.PropertyType);
    var converter = (ValueConverter)Activator
    .CreateInstance(converterType, this.GetService<IDataProtectionProvider>());

    b.Property(p.PropertyType, p.Name).HasConversion(converter);
    }
    else
    {
    ProtectedDataConverter converter = new ProtectedDataConverter(
    this.GetService<IDataProtectionProvider>());

    b.Property(typeof(string), p.Name).HasConversion(converter);
    }
    }
    }

    private class ProtectedDataConverter : ValueConverter<string, string>
    {
    public ProtectedDataConverter(IDataProtectionProvider protectionProvider)
    : base(
    s => protectionProvider
    .CreateProtector("personal_data")
    .Protect(s),
    s => protectionProvider
    .CreateProtector("personal_data")
    .Unprotect(s),
    default)
    {
    }
    }

    // You could get rid of this one if you only care about encrypting strings
    private class ProtectedDataConverter<T> : ValueConverter<T, string>
    {
    public ProtectedDataConverter(IDataProtectionProvider protectionProvider)
    : base(
    s => protectionProvider
    .CreateProtector("personal_data")
    .Protect(JsonSerializer.Serialize(s, default)),
    s => JsonSerializer.Deserialize<T>(
    protectionProvider.CreateProtector("personal_data")
    .Unprotect(s),
    default),
    default)
    {
    }
    }
    }

    最后,加密的责任很复杂,我建议您确保您牢牢掌握要进行的任何设置,以防止数据丢失等情况丢失您的加密 key 。此外,来自 OWASP 备忘单系列的 DotNet Security CheatSheet 是一个有用的阅读资源。

    关于c# - 使用 ASP.NET Core Entity Framework 在数据层中进行数据加密,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/54663388/

    27 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com