如何使用 EF Core 和 SQL Server 加密列?

问题描述 投票:0回答:1

我有一个使用 SQL Server 和 .NET 5、EF Core 5 的应用程序。我需要加密数据库中的一些敏感列。

我尝试了 Always Encrypted with Secure Enclaves 和 Symmetric Key encryption 等方法。

由于限制,我无法使用 Always Encrypted。参考:https://learn.microsoft.com/en-us/sql/relational-databases/security/encryption/always-encrypted-enclaves?view=sql-server-ver15#confidential-queries

然后我尝试在一个演示项目上使用对称密钥:

[Table("TestTableColumnEncrypt")]
public class TestTableColumnEncrypt
{
    public int Id { get; set; }
    public int NumberCol { get; set; }
    public string StrCol { get; set; }
    public decimal DecimalCol { get; set; }
    public int EncryptedNumberCol { get; set; }
    public string EncryptedStrCol { get; set; }
    public decimal EncryptedDecimalCol { get; set; }
}

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    ...
    modelBuilder.Entity<TestTableColumnEncrypt>().Property(x => x.EncryptedNumberCol)
        .HasConversion(x => this.Encrypt(x), y => Convert.ToInt32(this.Decrypt(y)));
    ...
}

public byte[] Encrypt(object value)
{
    var encryptedValue = this.Set<FakeEncryptModel>().FromSqlRaw("sp_OpenKeyAndEncryptColumnValue {0}", value.ToString()).AsEnumerable().FirstOrDefault().EncryptedValue;
    
    return (byte[])encryptedValue;
}

public object Decrypt(byte[] encryptedValue)
{
    var decryptedValue = this.Set<FakeDecryptModel>().FromSqlRaw("sp_DecryptColumnValue {0}", encryptedValue).AsEnumerable().FirstOrDefault().DecryptedValue;

    return decryptedValue;
}

static void Main(string[] args)
{
    TestContext ctx = new TestContext();
        
    if (!ctx.TestTableColumnEncrypts.Any())
    {
        var data = new List<TestTableColumnEncrypt>();

        foreach (var item in Enumerable.Range(1, 5))
        {
            data.Add(new TestTableColumnEncrypt
                {
                    NumberCol = 1 + item,
                    StrCol = "str" + item,
                    DecimalCol = 1M + item,
                    EncryptedNumberCol = 1 + item,
                    EncryptedStrCol = "str" + item,
                    EncryptedDecimalCol = 1M + item
                });
           ctx.Add(data[item - 1]);
       }

       ctx.SaveChanges();
   }
   else
   {
       // var data1 = ctx.TestTableColumnEncrypts.FromSqlRaw("Select * from dbo.TestTableColumnEncrypt").ToList(); //OK
       // var data2 = ctx.TestTableColumnEncrypts.ToList(); //OK
            
       var data3 = ctx.TestTableColumnEncrypts.Where(x => x.EncryptedNumberCol = 2).FirstOrDefault(); //NOT OK
        var data4 = ctx.TestTableColumnEncrypts.Where(x => x.EncryptedNumberCol > 2).FirstOrDefault(); //NOT OK
    }
}
    

我使用

EncryptByKey
DecryptByKey
函数进行加密和解密

插入数据成功。当我尝试获取表的所有数据时它也成功了。

但是当我将

where
条件与 LINQ 查询一起使用时,EF Core 会在转换过程中加密条件值 (2)。结果查询如下:

均等运算失败。 EncryptByKey 函数是不确定的,所以它总是为相同的明文生成不同的值。

SELECT TOP(1) [t].[Id], [t].[DecimalCol], [t].[EncryptedDecimalCol], [t].[EncryptedNumberCol], [t].[EncryptedStrCol], [t]. [NumberCol], [t].[StrCol]
从 [TestTableColumnEncrypt] AS [t]
WHERE [t].[EncryptedNumberCol] = 0x00ACD3D67A73667D4E640251A675D7D60200000022FDE885432CFE38FFBBBE6EC16E276D27817A2B0F73AB863D916F8A1CC2EFB5

SELECT TOP(1) [t].[Id], [t].[DecimalCol], [t].[EncryptedDecimalCol], [t].[EncryptedNumberCol], [t].[EncryptedStrCol], [t]. [NumberCol], [t].[StrCol]
从 [TestTableColumnEncrypt] AS [t]
WHERE [t].[EncryptedNumberCol] > 0x00ACD3D67A73667D4E640251A675D7D6020000008B26CC41884F22FB10231E2BC88AA7B439BC91EF5E71AD8DCA04EFF0C2AF2167

这里是问题所在,当我在 LINQ 查询中使用比较运算符时,SQL 比较两个加密值之间的数据,换句话说,它比较两个

VARBINARY(MAX)
值之间的数据。因此,比较操作失败。

如何在 LINQ 查询中的加密列上使用比较运算符?

您对可以使用原始 SQL 和 LINQ 查询检索数据的加密方法有什么建议吗?

c# sql-server entity-framework linq encryption
1个回答
0
投票

要使用 Entity Framework Core 和 SQL Server 加密列,您可以使用 SQL Server 的 Always Encrypted 功能。 Always Encrypted 使用存储在独立于数据库本身的安全位置的加密密钥来加密敏感数据。这确保即使有人获得了对数据库的访问权限,他们也看不到原始数据。

要将 Always Encrypted 与 Entity Framework Core 和 LINQ 查询一起使用,您可以在模型中定义加密列并告诉 EF Core 使用 Always Encrypted 提供程序。

这里是一个如何在模型类中定义加密列的例子:

public class Customer
{
    public int CustomerID { get; set; }
    
    [ColumnEncryption("CEK_Auto1", "SSN", CipherMode = CipherMode.AES_256_CBC, EncryptionType = EncryptionType.Randomized)]
    public string SSN { get;set; }

    public string Name { get; set; }
}

在这个例子中,SSN属性被标记为[ColumnEncryption]属性,表示应该使用指定的加密密钥(CEK_Auto1)和加密算法(AES_256_CBC)进行随机加密。

要在 LINQ 查询中使用此加密列,您可以使用 EF.Functions.Equals 方法而不是 == 运算符。这是一个例子:

var customer = db.Customers.Where(c => EF.Functions.Equals(c.SSN, "123-45-6789")).FirstOrDefault();

请注意,您需要下载并安装必要的 SQL Server 驱动程序并在服务器上启用 Always Encrypted 功能才能使用此功能。

有很多可用的加密算法,包括 AES 和 RSA,算法的选择将取决于您的具体需求和要求。选择加密算法时,重要的是要考虑性能影响、安全级别和密钥管理要求。您可能还需要考虑与可能需要访问加密数据的其他系统的兼容性。

© www.soinside.com 2019 - 2024. All rights reserved.