I have a Visual Studio 2015 ASP.NET Core 1.0 project that uses the Google Maps API. Currently, I could hard code the API key in two places:
1) the script tag of a view e.g.
<script src="https://maps.googleapis.com/maps/api/js?key=YOUR_API_KEY_HERE"></script>
2) the config.json file of the project e.g.
{
"Keys": { "GoogleMapsAPIKey": "YOUR_API_KEY_HERE" }
}
Either way the key is exposed when using source control so why not use a Windows Environment Variable?
Within Windows System Properties I added a new environment variable called GoogleMapsAPIKey and pasted in the actual key.
Now how do I use this environment variable in either the script tag or in config.json?
The purpose of the question is to get a general answer on how to use environment variables to hide API keys in such situations.
Add the environment variables configuration provider. Example from here:
public Startup(IHostingEnvironment hostingEnvironment)
{
var builder = new ConfigurationBuilder()
.SetBasePath(hostingEnvironment.ContentRootPath)
.AddJsonFile("config.json")
.AddEnvironmentVariables();
}
If you just want to hide the key during development, you can use user secrets.
Related
I have earlier achieved this .net 3.1. But it couldn't be possible with .Net 6 because of startup.cs removed.
I have registered a few services,
builder.Services.AddControllers();
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
var appSettings = builder.Configuration.GetSection("AppSettings").Get<AppSettings>();
builder.Services.AddScoped<IEncryption, Encryption>();
//Here I need to get the IEncryption Service, and call the method in this service to encrypt/decrypt the connection string to pass to DBContext Service.
builder.Services.AddDbContext<CatalogDbContext>(options => options.UseNpgsql(
appSettings.ConnectionString));
var app = builder.Build();
Earlier in .NET 3.1, I used BuildServicProvider() to get the Encryption service, and call the methods in that service to do the required logic then got the proper connection string I wanted that would be passed to the DBContext service on the next line.
Now, .NET 6/7 is forced to use the services only after app = builder.Build(); so, I can't register the DBCOntext after the build() method.
How can I solve this case? Any recommended approach to do this in .NET 6/7?
You still can useStartup.cs in .net 6
var builder = WebApplication.CreateBuilder(args);
var startup = new Startup(builder.Configuration);
startup.ConfigureServices(builder.Services); // calling ConfigureServices method
var app = builder.Build();
startup.Configure(app, builder.Environment); // calling Configure method
And then you can use ConfigureServices and Configure methods to register your services before building.
You didn't need to use BuildServiceProvider in .NET Core 3.1 either. AddDbContext has an overload that provides access to an IServiceProvider instance :
builder.Services.AddDbContext<CatalogDbContext>((services,options) =>{
var myOwnDecrypter=services.GetRequiredService<IMyOwnDecrypter>();
var cns=myOwnDecrypter.Decrypt(appSettings.ConnectionString,key);
options.UseNpgsql(cns);
});
or, if you use the ASP.NET Core Data Protection package :
builder.Services.AddDataProtection();
...
builder.Services.AddDbContext<CatalogDbContext>((services,options) =>{
var protector = services.GetDataProtector("Contoso.Example.v2");
var cns=protector.Unprotect(appSettings.ConnectionString);
options.UseNpgsql(cns);
});
or, if IConfiguration.GetConnectionString is used :
builder.Services.AddDataProtection();
...
builder.Services.AddDbContext<CatalogDbContext>((services,options) =>{
var conn_string=services.GetService<IConfiguration>()
.GetConnectionString("MyConnectionString");
var protector = services.GetDataProtector("Contoso.Example.v2");
var cns=protector.Unprotect(conn_string);
options.UseNpgsql(cns);
});
That said, it's the configuration provider's job to decrypt encrypted settings, not the service/context's. ASP.NET Core's configuration allows using multiple different configuration sources in the same host, not just a single settings file. There's nothing special about appsettings.json. That's just the default settings file name.
You can add another settings file with sensitive contents with AddJsonSettings. That file could use the file system's encryption, eg NTFS Encryption, to ensure it's only readable by the web app account
You can read settings from a key management service, like Hashicorp, Azure Key Vault, Amazon Key Management etc.
You can create your own provider that decrypts its input. The answers to this SO questino show how to do this and one of them inherits from JsonConfigurationProvider directly.
Important Caveat: In general, my suggestion below is a bad practice
Do not call BuildServiceProvider
Why is bad? Calling BuildServiceProvider from application code results in more than one copy of singleton services being created which might result in incorrect application behavior.
Justification: I think it is safe to call BuildServiceProvider as long as you haven't registered any singletons before calling it. Admittedly not ideal, but it should work.
You can still callBuildServiceProvider() in .Net6:
builder.Services.AddScoped<IEncryption, Encryption>();
// create service provider
var provider = builder.Services.BuildServiceProvider();
var encryption = scope.ServiceProvider.GetService<IEncryptionService>();
// use service here
or alternatively
builder.Services.AddScoped<IEncryption, Encryption>();
var provider = builder.Services.BuildServiceProvider();
using (var scope = provider.CreateScope()) {
var encryption = scope.ServiceProvider.GetService<IEncryptionService>();
// use service here
}
Alternative:
You can still use the classic startup structure in .Net6/7. We upgraded our .Net3.1 projects to .Net6 without having to rewrite/restructure the Startup()
Let's say I have connection string for Development environment specified in appsettings.Development.json and connection string for the Staging environment specified in appsettings.Staging.json
All I need to do to switch between Development and Staging is to navigate to Visual Studio Debug tab in project properties and change the value for ASPNETCORE_ENVIRONMENT environment variable.
Now, of course I don't want to have connection string in appsettings.*.json for security reasons. So I move it to User Secrets.
Problem is - it seems there is just one secrets.json file that is used by all the environments. There are no secrets.Development.json or secrets.Staging.json. This means after I switch from Development to Staging environment via Visual Studio Debug tab I then also need to change connection strings manually in secrets.json which kind of defeats the purpose of having built-in support for the environments.
Is this correct that User Secrets are not supported on per-environment basis? If so - is there another approach that would avoid having to modify Secret connection string manually when switching environments?
If you check the tool's parameters with dotnet user-secrets --help you'll see you can specify different secrets per configuration (Debug, Release, any other you want) but not per environment. Which is not a bad decision if you think about it.
The ASPNETCORE_ENVIRONMENT environment variable is meant to tell your application whether the current machine or container is a Development, Production or other environment, so it can pick the appropriate settings file. This environment variable isn't expected to change from one application execution to the next. Even when using containers, the environment variables are passed from the host to the container and aren't expected to change during the container's lifetime.
The secrets files are supposed to be per machine, for development purposes, so there's no need to keep separate files per environment. It makes much more sense to use separate files for configuration, allowing developers to simply change from Dev to Release or Testing or any other custom configuration they may have.
Specifying secrets per configuration
The dotnet user-secrets tool works by reading the UserSecretsId value from the project file and storing the secrets in a JSON file with the same name, eg c952ecfc-344e-43e1-bb67-1ac05973d6c6.json. It's possible to store a UserSecretsId for each configuration.
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
<UserSecretsId>c952ecfc-344e-43e1-bb67-1ac05973d6c6</UserSecretsId>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
<UserSecretsId>7D104000-2230-4EDE-8AE6-63BDDA0BD0C5</UserSecretsId>
</PropertyGroup>
When the -c parameter is used to specify a configuration, the user-secrets tool will read the UserSecretsId value from the corresponding section and use it to store or read secrets.
The dotnet user-secrets init command doesn't recognize the -c parameter, so the csproj file needs to be modified directly.
Once that's done, one can set and read secrets by specifying the configuration, eg :
❯ dotnet user-secrets set -c Debug Key1 Value1
Successfully saved Key1 = Value1 to the secret store.
❯ dotnet user-secrets set -c Release Key1 Value2
Successfully saved Key1 = Value2 to the secret store.
❯ dotnet user-secrets list -c Debug
Key1 = Value1
❯ dotnet user-secrets list -c Release
Key1 = Value2
I also needed this and I think I've come up with an elegant solution.
secrets.json file is shared among all environments you are using, what you can do is add the environment parent to each node in the file and then do the little trick (last 2 code fragments).
Suppose you have a configuration, e.g. in appsettings.json or appsettings.{environment}.json:
{
"Key1": "value1",
"Secret1": "<set yourself>"
}
and then you have the secret part in secrets.json:
{
"Secret1": "my secret value"
}
you can get and bind the whole section easily:
IConfiguration configuration = new ConfigurationBuilder()
.AddJsonFile("appsettings.json")
.AddJsonFile($"appsettings.{environment}.json")
.AddUserSecrets<Program>()
.Build();
var myConfiguration = configuration.Get<MyConfiguration>();
Now comes the problem, I want to have multiple environments in the secrets.json. I personally used to have secrets for all environments I needed and just comment/uncomment what I wanted, however, it's manual work. So I will prefix them with environment name instead.
{
"Development:Secret1": "my secret development value",
"Staging:Secret1": "my secret staging value"
}
You have to load the environment-specific configuration(s) from the IConfiguration instance and override the existing myConfiguration values using:
configuration.GetSection(environment).Bind(myConfiguration);
\\ or
configurationRoot.Bind(environment, configuration);
And that's it.
If you run it using environment="Development", you will have "my secret development value" loaded. If you run it using environment="Staging", you will have "my secret staging value" loaded.
Additional details
The double dot character (:) acts as a new section, so if you write
{
"Development:Secret1": "my secret development value",
"Staging:Secret1": "my secret staging value"
}
it's the same as
{
"Development":
{
"Secret1": "my secret development value"
},
"Staging":
{
"Secret1": "my secret staging value"
}
}
the trick is about loading just the environment-specific part and binding it to the myConfiguration instance.
I must mention that no matter what environment you are using, all the secrets are actually loaded in the memory.
The Secret Manager (https://learn.microsoft.com/en-us/aspnet/core/security/app-secrets?view=aspnetcore-3.1) is designed strictly for development, not any other stage (environment), since it is inherently insecure (local dev secrets are not encrypted). See the warning on the page linked. So there is no need to have per environment secrets storage vis-a-vis that tool. For other environments (staging, prod, etc), Microsoft would likely steer you toward their secure secrets storage service -- Key Vault. You can use the Secret Manager for dev secrets and then store the other environments in Key Vault. I have done this in many Asp.Net Core apps and it works well. For Key Vault info, see this:
https://learn.microsoft.com/en-us/aspnet/core/security/key-vault-configuration?view=aspnetcore-3.1
There is no "out of the box" way to use different secrets per environment. However you can use the options pattern to bind your configs depending on environment:
Create a secrets.json with your environments at the top level:
{
"Local": {
"Secret1": "local secret",
"ConnectionStrings": {
"DB": "LocalDBConnectionstring"
}
},
"Development": {
"Secret1": "dev secret",
"ConnectionStrings": {
"DB": "DevDBConnectionstring"
}
}
}
Create classes to map your secret configurations:
public class SecretConfigurationEnvironment
{
public string secret1 { get; set; }
public ConnectionStringsConfig ConnectionStrings { get; set; }
}
public class ConnectionStringsConfig
{
public string DB { get; set; }
}
Map the secrets to your classes in your program.cs:
var builder = WebApplication.CreateBuilder(args);
builder.Services.Configure<SecretConfigurationEnvironment>(
builder.Configuration.GetSection(builder.Environment.EnvironmentName));
Use your configs with dependencyInjection whereever you need them:
public class MyClass {
private readonly IOptions<SecretConfigurationEnvironment> _secretConfig;
public MyClass(IOptions<SecretConfigurationEnvironment> secretConfig) {
_secretConfig = secretConfig
}
public void MyMethod() {
var envDBConnectionString = _secretConfig.value.Connectionstrings.DB
}
}
Now your configs will change depending on your environment and you can still use all your configs stored in the appsettings.environment.json files (also with options if you like)
That being said, here is the usual disclaimer: Don't use secrets.json for staging or production secrets.
The code i provided is for .net 6, wep-api. Older versions or different project-types are very similar though and can be found at the microsoft documentation.
I am developing a mobile application using Xamarin.Forms
I had the following Home page contains login info:
How can we have the application to automatically save the user name, so that they do not have to type it in each time (as in a browser)?
You can use Properties dictionary in Xamarin.Forms Application class. And let the Xamarin.Forms framework handle persisting user name between app restarts and pausing/resuming your app.
Save user name by writing it to Properties dictionary
var properties = Xamarin.Forms.App.Current.Properties;
if(!properties.ContainsKey("username")
{
properties.Add("username", username);
}
else
{
properties["username"] = username;
}
Then, when your login screen is about to appear (for example in OnAppearing method) check Properties for user name:
var properties = Xamarin.Forms.App.Current.Properties;
if(properties.ContainsKey("username")
{
var savedUsername = (string)properties["username"];
}
If it's not there, then it means that this is first time when user log in into your application.
A very similar question was posed just a few days ago - my answer on that question also applies to your question: The best way to save Configuration data in Xamarin.Forms based app?
Essentially, you want to store the information using the native settings functionality. I would advise against using Application.Properties for now. It is currently not reliable on Android, and in the past has had other problems. The nuget package referenced in my linked answer is a better approach and will save you some headache in the future.
The right way to be done is through the App settings plugin
https://github.com/jamesmontemagno/Xamarin.Plugins/tree/master/Settings
What i did in my application is.
1) Installed Plugin.Settings from nuget
2)Added to Helpers->Settings.cs (autogenerated file by plugin) the following
public static class Settings
{
private static ISettings AppSettings
{
get { return CrossSettings.Current; }
}
private const string UserNameKey = "username_key";
private static readonly string UserNameDefault = "demo";
public static string UserName
{
get { return AppSettings.GetValueOrDefault<string>(UserNameKey, UserNameDefault); }
set { AppSettings.AddOrUpdateValue<string>(UserNameKey, value); }
}
}
3)In order to keep the username in the Application Context set
Settings.UserName = ViewModel.Username;
4)When you login screen starts
string username = Settings.UserName;
The answer is simple: persistance. Servers do this by setting cookies containing the data (or reference to it) that they want you to see when rendering the form field.
In order to do this in an app (with Xamarin for instance), you need to store the user's data into a file or database somewhere. Since you're using Xamarin you can probably use some sort of ConfigurationManager to keep track of this.
Obviously you could just create a config file in the local storage you have for your app (I don't think you need permissions to create files in that space).
When you have the info stored somewhere, just retrieve it and set the input's value to it.
My goal is to have appsettings.json file with production configurations and have possibility to costomize it for every developer, e.g. use local connection strings. So it does not similar to transform web.config mechanism, i don't want depends on bulid configuration. Can anyone provide solution for this goal?
In one of my past project we do so: we store all configure information in custom config.xml and parsed it into the custom structure. Web.config contains only server configaration. every developer has own copy of config files with his own data. Solution is that application use configuration files from path, that specified in environment path in windows via Environment.GetEnvironmentVariable("key").
Does anyone have idea better than my one?
This is how I manage configuration: see comments in the code
public Startup(IHostingEnvironment env, IApplicationEnvironment appEnv)
{
var builder = new ConfigurationBuilder()
.AddJsonFile("appsettings.json"); // this one has default configuration
// this file name is added to my gitignore so it won't get committed,
// I keep local dev configuration there
builder.AddJsonFile("appsettings.local.overrides.json", optional: true);
if (env.IsDevelopment())
{
// This reads the configuration keys from the secret store.
// if you need a more secure place for dev configuration use usersecrets
// For more details on using the user secret store see http://go.microsoft.com/fwlink/?LinkID=532709
builder.AddUserSecrets();
}
// the order in which config sources is added is important, a source added later
// will override the same settings from a source added before
// environment variables is usually for production and therefore added last to give it higher priority
builder.AddEnvironmentVariables();
Configuration = builder.Build();
}
I have a simple Xamarin Forms app. I've now got a simple POCO object (eg. User instance or an list of the most recent tweets or orders or whatever).
How can I store this object locally to the device? Lets imagine I serialize it as JSON.
Also, how secure is this data? Is it part of Keychains, etc? Auto backed up?
cheers!
You have a couple options.
SQLite. This option is cross-platform and works well if you have a lot of data. You get the added bonus of transaction support and async support as well. EDIT: In the past I suggested using SQLite.Net-PCL. Due to issues involving Android 7.0 support (and an apparent sunsetting of support) I now recommend making use of the project that was originally forked from: sqlite-net
Local storage. There's a great nuget that supports cross-platform storage. For more information see PCLStorage
There's also Application.Current.Properties implemented in Xamarin.Forms that allow simple Key-Value pairs of data.
I think you'll have to investigate and find out which route serves your needs best.
As far as security, that depends on where you put your data on each device. Android stores app data in a secure app folder by default (not all that secure if you're rooted). iOS has several different folders for data storage based on different needs. Read more here: iOS Data Storage
Another option is the Xamarin Forms settings plugin.
E.g. If you need to store a user instance, just serialize it to json when storing and deserialize it when reading.
Uses the native settings management
Android: SharedPreferences
iOS: NSUserDefaults
Windows Phone: IsolatedStorageSettings
Windows RT / UWP: ApplicationDataContainer
public User CurrentUser
{
get
{
User user = null;
var serializedUser = CrossSettings.Current.GetValueOrDefault<string>(UserKey);
if (serializedUser != null)
{
user = JsonConvert.DeserializeObject<User>(serializedUser);
}
return user;
}
set
{
CrossSettings.Current.AddOrUpdateValue(UserKey, JsonConvert.SerializeObject(value));
}
}
EDIT:
There is a new solution for this. Just use Xamarin.Essentials.
Preferences.Set(UserKey, JsonConvert.SerializeObject(value));
var user= JsonConvert.DeserializeObject<User>(Preferences.Get(UserKey, "default_value");
Please use Xamarin.Essentials
The Preferences class helps to store application preferences in a key/value store.
To save a value:
Preferences.Set("my_key", "my_value");
To get a value:
var myValue = Preferences.Get("my_key", "default_value");
If you want to store a simple value, such as a string, follow this Example code.
setting the value of the "totalSeats.Text" to the "SeatNumbers" key from page1
Application.Current.Properties["SeatNumbers"] = totalSeats.Text;
await Application.Current.SavePropertiesAsync();
then, you can simply get the value from any other page (page2)
var value = Application.Current.Properties["SeatNumbers"].ToString();
Additionally, you can set that value to another Label or Entry etc.
SeatNumbersEntry.Text = value;
If it's Key value(one value) data storage, follow below code
Application.Current.Properties["AppNumber"] = "123"
await Application.Current.SavePropertiesAsync();
Getting the same value
var value = Application.Current.Properties["AppNumber"];