如何加密/解密appsettings.json中的连接字符串?

Gab*_*bic 6 c# security encryption asp.net-core asp.net-core-webapi

我正在开发 ASP.Net Core Web API,在开发环境中,我们只需在文件中以纯文本形式编写连接字符串appsettings.json。但现在我们要将此 API 发布到我们的生产服务器和 Web(我们将使用 FortiWeb 来发布 API,而不是 Azure),因此我们不能再以纯文本形式提供用户凭据。

我一直在寻找保护/加密连接字符串的方法,但大多数解决方案使用 Azure Key Vault 或用户机密。我决定按照这篇文章来加密和解密appsettings.json信息,但它有点不完整。我设法创建了文章中描述的类,但我陷入困境并且不知道如何使用它。这是我第一次完全从头开始开发 API,而且我在信息安全方面完全是新手。

这是我到目前为止所拥有的:

存档appsettings.json

{
  "ConnectionStrings": {
    "MyConnection": "Data Source=MyServerName;Initial Catalog=MyDb;Persist Security Info=True;User ID=MyUser;Password=MyPwd"
  },
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft": "Warning",
      "Microsoft.Hosting.Lifetime": "Information"
    }
  },
  "AllowedHosts": "*"
}
Run Code Online (Sandbox Code Playgroud)

上课时DecryptedConfiguration.cs

public class DecryptedConfiguration : ConfigurationProvider
{
    public ICryptoTransform Decryptor;
    private ICryptoTransform Encryptor;

    internal DecryptedConfiguration(byte[] Key, byte[] IV)
    {
        Aes aes = Aes.Create();
        Decryptor = aes.CreateDecryptor(Key, IV);
        Decryptor = aes.CreateDecryptor(Key, IV);
    }

    public override bool TryGet(string key, out string value)
    {
        if (base.TryGet(key, out value))
        {
            byte[] decryptedBytes = Convert.FromBase64String(value);
            byte[] textBytes = Decryptor.TransformFinalBlock(decryptedBytes, 0, decryptedBytes.Length);
            value = Encoding.Unicode.GetString(textBytes);
            return true;
        }
        return false;
    }

    public override void Set(string key, string value)
    {
        byte[] textBytes = Encoding.Unicode.GetBytes(value);
        byte[] decryptedBytes = Decryptor.TransformFinalBlock(textBytes, 0, textBytes.Length);
        base.Set(key, Convert.ToBase64String(decryptedBytes));
    }
}
Run Code Online (Sandbox Code Playgroud)

上课时DecryptedConfigurationSource.cs

public class DecryptedConfigurationSource : IConfigurationSource
{
    private byte[] Key;
    private byte[] IV;

    public DecryptedConfigurationSource(byte[] Key, byte[] IV)
    {
        this.Key = Key;
        this.IV = IV;
    }

    public IConfigurationProvider Build(IConfigurationBuilder builder)
    {
        return new DecryptedConfiguration(Key, IV);
    }
}
Run Code Online (Sandbox Code Playgroud)

上课时Startup.cs

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
    ...

    Aes aes = Aes.Create();
    byte[] key = aes.Key;
    byte[] iv = aes.IV;

    IConfigurationBuilder encryptingBuilder = new ConfigurationBuilder()
        .AddJsonFile("appsettings.json")
        .Add(new DecryptedConfigurationSource(key, iv))
        .AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true);
    IConfiguration cfg = encryptingBuilder.Build();

    string ecryptedValue = cfg.GetValue<string>("ConnectionStrings:MyConnection");
}
Run Code Online (Sandbox Code Playgroud)

mr1*_*100 3

作为一个简短的提示,我建议将值存储在环境变量中。这比将它们简单地存储在文本文件中更安全,因为访问环境变量比读取文件需要更高的权限。对于许多公司来说,如果无法使用 azure key Vault 或 aws Secret Manager,这已经足够了。如果您将应用程序托管为 docker 容器或在虚拟机上运行,​​则可以使用这两种环境变量。这里有一些关于易于使用的解决方案的文章,尽管生产文章建议使用 azure key Vault https://learn.microsoft.com/en-us/aspnet/core/security/app-secrets?view=aspnetcore-6.0 &选项卡=窗口

作为替代方案,您可以考虑使用本文中描述的 Secret 管理器提供程序。

我不建议按照文章建议的方式进行操作,因为这涉及在某个地方永久存储加密密钥(文章提到您不应该调用 Aes aes = Aes.Create(); 因为每次调用它时都会生成新密钥,您将需要一个永久的密钥一)。但是要确保它的安全,您必须从像天蓝色密钥库这样安全的东西加载它,如果您在代码中拥有它,那么有人可以下载您的程序集,反编译它,访问存储的密钥,并可以自行解密您的所有秘密。如果我的理解是正确的,那么本文中提出的解决方案是“通过模糊实现安全性”而不是实际的解决方案 - 它只会使访问机密变得更加困难,但仍然完全可以管理,就像您将机密放在 appsettings.json 中一样。

  • 不,我不是这个意思。正如我所说,如果没有秘密管理服务,我最会推荐环境变量。根据经验,开发人员无权访问生产系统会更好,因此如果您自己无权访问它们总是更好。似乎秘密管理器可能尚未加密配置,因此它可能不是最好的解决方案。 (2认同)
  • 如果您仍在加密之后,这里有一些有趣的库 https://github.com/devattic/ConfigCrypter。尽管创建者声称这更多的是让黑客获取秘密变得更加困难,而不是不可能。 (2认同)
  • 看看这个 https://docs.fortinet.com/document/fortisoar/1.0.0/thycotic-secret-server/1/thycotic-secret-server-v1-0-0 FortiNet 有自己的保险库。你应该使用它。 (2认同)