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

5
我正在开发一个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": "*"
}

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));
    }
}

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);
    }
}

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");
}

问题具体是什么? - Rezo Megrelidze
2
问题是:我该如何完成连接字符串加密?使用文章中描述的方法还是不使用? - Gabic
2个回答

2
作为简短的提示,我建议将值存储在环境变量中。这比将它们明文存储在文本文件中更安全,因为访问环境变量需要比读取文件更高的特权。对于许多公司来说,如果无法使用 Azure 密钥保管库或 AWS 秘密管理器,则此解决方案已足够。您可以在作为 Docker 容器托管应用程序或在 VM 上运行应用程序时都可以使用环境变量。这里有一些易于使用的解决方案文章,尽管生产文章建议使用 Azure 密钥保管库:https://learn.microsoft.com/en-us/aspnet/core/security/app-secrets?view=aspnetcore-6.0&tabs=windows 作为替代方案,您可以考虑使用文章中描述的 Secret Manager 提供程序。
我不建议按照文章建议进行操作,因为这涉及永久存储加密密钥(文章提到您不应该调用 Aes aes = Aes.Create(); 因为每次调用都会生成新密钥,您需要一个永久的密钥)。但要使其安全,您必须从像 Azure 密钥保管库这样的安全位置加载它。如果您将其放在代码中,某人可以下载您的程序集,反编译它,访问存储的密钥,并可以自行解密所有机密信息。如果我理解正确,文章提出的解决方案是“安全性通过隐蔽性”而不是实际解决方案-它只使访问机密信息变得更加困难,但仍然完全可管理,就像将机密信息放在 appsettings.json 中一样。

1
不,这不是我想表达的。正如我所说,如果没有机密管理服务,我会最推荐环境变量。通常来说,开发人员无法访问生产系统更好,因此最好不要自己访问它们。似乎机密管理器可能尚未对配置进行加密,因此这可能不是最佳解决方案。 - mr100
这是目前为止最好的解决方案,因为我找不到一种加密方法,我能够理解如何使用。 - Gabic
1
如果你仍然需要加密,这里有一个有趣的库 https://github.com/devattic/ConfigCrypter。尽管创建者声称它更多地是使黑客获取秘密变得更加困难,而不是不可能。 - mr100
1
看一下这个链接:https://docs.fortinet.com/document/fortisoar/1.0.0/thycotic-secret-server/1/thycotic-secret-server-v1-0-0。FortiNet有他们自己的保险库,你应该使用它。 - mr100
关于Secret Manager,提到的文章指出:“Secret Manager工具不会加密存储的秘密信息,也不应被视为可信存储。它仅用于开发目的。密钥和值存储在用户配置文件目录中的JSON配置文件中。”因此,我并不认为这是一个真正好的替代方案。该文章还警告不要使用环境变量。 - peter70
显示剩余3条评论

0
多年来,我阅读了许多关于存储明文连接字符串的恐怖故事。但我总是会问自己,“我们要保护自己免受谁的攻击?”如果是内部威胁,那么这是一个很好的论点,但我认为系统管理员可以更好地限制内部威胁。我曾在许多地方工作过,这些地方允许开发人员完全访问数据库,不需要连接字符串。如果我们担心外部黑客,web.config和AppSetting.json文件被锁定在服务器端的IIS深处。如果黑客设法突破到达IIS的这个深度,那么游戏已经结束,他们已经拥有了王国的钥匙。
我觉得为经理们保护连接字符串很有趣,但总有一个漏洞存在。第三方密钥库API可能是最安全的,但密码仍然需要存储在某个地方,而且你真的想要依赖密钥库吗?通过C#加密/解密可以工作,但总有人会指出,“现在密钥在代码中并存储在git上”!是的,我们只是成功地让它对黑客更加复杂,从而消除了懒惰的黑客。同样,如果黑客能够到达那个程度,他们真的需要连接字符串吗?

那么有意义吗?当然要保持警惕,在任何可能的地方保护您的代码,特别是客户端。我认为还有很多其他漏洞存在,这些漏洞带来的风险更大,值得关注。


虽然具有挑衅性的评论,但并没有尝试回答原始问题,因此不应该作为答案发布。 - Jeremy Caney

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接