在ASP.NET Core中存储生产秘密

时间:2016-10-19 12:40:39

标签: asp.net-core

我试图弄清楚哪里可以最好地存储ASP.NET Core应用程序的应用程序生产机密。有两个类似的问题 Where should I store the connection string for the production environment of my asp.net-5 app?How to deploy ASP.NET Core UserSecrets to production 这两者都建议使用环境变量。

我的问题是我想运行具有不同数据库和不同数据库凭据的Web应用程序的多个实例。所以应该有一些包含秘密的每个实例配置。

如何以安全的方式实现这一目标?

请注意,应用程序应该能够自我托管并在IIS下托管! (稍后我们还计划在Linux上运行它,如果这对问题有任何重要性的话)

更新

这个问题与尝试在生产中使用ASP.NET用户机密无关! UserSecrets被排除用于生产。

2 个答案:

答案 0 :(得分:6)

正如他们所述,用户机密用于开发(以避免意外地将凭据提交到SCM中)而不是用于生产。您应该为每个数据库使用一个连接字符串,即ConnectionStrings:CmsDatabaseProductionConnectionStrings:CmsDatabaseDevelopment等。

或者使用docker容器(当您不使用Azure App Service时),然后您可以按容器设置它。

或者,您也可以使用基于环境的appsetting文件。 appsettings.production.json,但它们不得包含在源代码管理(Git,CSV,TFS)中!

在启动时只需:

    public Startup(IHostingEnvironment env)
    {
        var builder = new ConfigurationBuilder()
            .SetBasePath(env.ContentRootPath)
            .AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
            .AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true)
            .AddEnvironmentVariables();
        Configuration = builder.Build();
    }

这样,您可以从appsettings.production.json加载特定内容,并且仍然可以通过环境变量覆盖它。

答案 1 :(得分:2)

没有Azure Vault或任何第三方提供商,有一种简单的方法可以安全地在生产中存储应用程序秘密:

  1. 通过运行应用程序的-config开关来输入应用程序机密,例如:dotnet helloworld -config;在Program.Main中,检测到此开关以允许用户输入机密并将其存储在单独的加密的.json文件中:
    public class Program
    {
        private const string APP_NAME = "5E71EE95-49BD-40A9-81CD-B1DFD873EEA8";
        private const string SECRET_CONFIG_FILE_NAME = "appsettings.secret.json";

        public static void Main(string[] args)
        {
            if (args != null && args.Length == 1 && args[0].ToLowerInvariant() == "-config")
            {
                ConfigAppSettingsSecret();
                return;
            }
            CreateWebHostBuilder(args).Build().Run();
        }

        public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
            WebHost.CreateDefaultBuilder(args)
                .ConfigureAppConfiguration((builder, options) =>
                {
                    options.AddJsonFile(ConfigFileFullPath, optional: true, reloadOnChange: false);
                })
                .UseStartup<Startup>();

        private static void ConfigAppSettingsSecret()
        {
            var serviceCollection = new ServiceCollection();
            AddDataProtection(serviceCollection);
            var services = serviceCollection.BuildServiceProvider();
            var dataProtectionProvider = services.GetService<IDataProtectionProvider>();
            var protector = CreateProtector(dataProtectionProvider);

            string dbPassword = protector.Protect("DbPassword", ReadPasswordFromConsole());
            ... // other secrets
            string json = ...;  // Serialize encrypted secrets to JSON
            var path = ConfigFileFullPath;
            File.WriteAllText(path, json);
            Console.WriteLine($"Writing app settings secret to '${path}' completed successfully.");
        }

        private static string CurrentDirectory
        {
            get { return Directory.GetParent(typeof(Program).Assembly.Location).FullName; }
        }

        private static string ConfigFileFullPath
        {
            get { return Path.Combine(CurrentDirectory, SECRET_CONFIG_FILE_NAME); }
        }

        internal static void AddDataProtection(IServiceCollection serviceCollection)
        {
            serviceCollection.AddDataProtection()
                .SetApplicationName(APP_NAME)
                .DisableAutomaticKeyGeneration();
        }

        internal static IDataProtector CreateProtector(IDataProtectionProvider dataProtectionProvider)
        {
            return dataProtectionProvider.CreateProtector(APP_NAME);
        }
    }
  1. 在Startup.cs中,读取并解密机密:
public void ConfigureServices(IServiceCollection services)
{
    Program.AddDataProtection(services);
    ...
}

public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
    ...
    if (env.IsProduction())
    {
        var dataProtectionProvider = app.ApplicationServices.GetService<IDataProtectionProvider>();
        var protector = Program.CreateProtector(dataProtectionProvider);
        var builder = new SqlConnectionStringBuilder();
        builder.Password = protector.Unprotect(configuration["DbPassword"]);
        ...
    }
}

就是这样!