I would like to use my connectionString without it showing in a code. Currently it is in the appsettings.json. Is there a simple as non-complicated solution as possible?
-
Do you mean connection-string as a db-connection string? Are you using EntityFrame-Core? Are you using "AddDbContext" (with a MyCustomAppDbContext)?granadaCoder– granadaCoder2023-07-25 14:48:12 +00:00Commented Jul 25, 2023 at 14:48
-
Can you share the motivation for "hiding" the connection string, are you doing so to ensure users don't know the database credentials, or are you doing so to make changing the connection string easier, etc.?Matthew– Matthew2023-07-25 15:07:13 +00:00Commented Jul 25, 2023 at 15:07
-
This is why people participate in StackOverflow. To learn , I didn't think that the connection string needed to be protecteduser16612111– user166121112023-07-25 17:08:03 +00:00Commented Jul 25, 2023 at 17:08
-
@QingGuo it only does if you have sensitive data in it. This depends on the database provider and the type of connection. Some providers will force you to put username/password pairs in the connection string, and you don't want that to be visible in source control.julealgon– julealgon2023-07-25 18:49:15 +00:00Commented Jul 25, 2023 at 18:49
-
1@Matthew hi, I am hidding it for it has DB user with a password. Also I work for a corporate and there are corporate policies in place. I am not forced to obey those to a point now, since my app is a small insignificant project for few workers that deals with a test database of no significance either. But I would like to practise more strict policies now before I get onto larger projects. Also my senior developer told me to do so (haha).Matěj– Matěj2023-07-26 06:53:40 +00:00Commented Jul 26, 2023 at 6:53
2 Answers
So while your question appears on the surface to be "trivial".. you are entering the world of "how do I keep my secrets secure".
Also:
So.. "not complicated" ... might also be labeled as "easy"....and "easy" are at odds with "secure my db-password-value". Security .. IS inconvenient.
Short Version:
(1) I show a link to a microsoft article that has a few options.
(2) I give you some ideas/pointers I've either done or considered in the past.
The biggest idea I present is "store the secret value in something that is meant to store secrets" (azure key vault, hashicorp-vault for example)
(3) I show how you might "wire up your db-context" using the value you get from (the something that is meant to store secrets).
Longer-Version:
First, a microsoft article about it:
https://learn.microsoft.com/en-us/aspnet/core/security/app-secrets?view=aspnetcore-7.0&tabs=windows
It talks about Environment Variables and "Secret Manager".
But I will add OTHER possibilities that the M$ article does not mention.
So I will make a few assumption since your question does not contain some information. At the same time, you have tagged it with asp.net CORE (6.0). (Emphasis on the CORE).
Let's say you are using
//somewhere you are
//IServiceCollection services
services.AddDbContext<MyCustomApplicationDbContext>(options =>
options.UseSqlServer(
Configuration.GetConnectionString("DefaultConnection")));
..
Note the code
Configuration.GetConnectionString("DefaultConnection")
This is the "easy peezey" way of getting the connection-string that will drive the db-context.
If you are not using "integrated-security" for MsSqlServer .. or you are using a different concrete rdbms provider (PostGres for example) .. and you have to have a db-password "somewhere"...then you are correct..it is "too easy" to see the db-password in the appSettings.json
So what can you do?
Well, you could use a "real" secrets-retriever.
What do I mean?
You can either read the entire-connection-string (or just the sensitive value of the db-password)...using code that talks to something like:
AzureKeyVault.
Hashicorp Vault.
or in a Kubernetes world... read a "mounted secret".
or.. as one answer briefly mentions... read an environment variable.
And then in our IoC registrations, use this value.
If you store the entire connection string, then you can use it.
If you store only the db-password..then you can put non sensitive values in your appSettings.json and read them one by one..and then piece meal it all together with
....
What do I have?
I wrote an interface ("my" code).
public ISecretsReader
public SecureString readSecret(String secretName)
I have two concretes.
one of them is a
LocalDeveloperSecretsReader : (implements) ISecretsReader
It will read the connection string .. using appSettings.Development.json
//start side note about optionally reading a "my environment" appSettings.___.json file //
string myEnvironmentName = /* code to read the environment variable, either "DOTNETCORE_ENVIRONMENT" or "ASPNETCORE_ENVIRONMENT" */
ConfigurationBuilder builder = new ConfigurationBuilder()
.SetBasePath(env.ContentRootPath)
.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
.AddJsonFile($"appsettings.{myEnvironmentName}.json", optional: true)
IConfiguration config = bulder.Build();
//end side note
...
Then I have a different concrete:
AzureKeyVaultSecretReader : ISecretsReader
(your concrete can be whatever "secret store" you want to use.
And this concrete talks to AzureKeyVault to get the value.
Be warned.... somewhere you have to "secure" the code that reads from AzureKeyVault. (we call this the key-zero issue)....
My team has solved the "key zero" issue a Service Principal.
see:
...
And so I use
to IoC register ONE of the concretes, not both. Local-Developers get the easy-peezey concrete. The deployed code gets the Azure Key Vault Secret Reader.
But my code...... will read the secret-value, and I use sqlconnectionstringbuilder to "build up" my connection string.
and then I add to the IoC container ("IServiceCollection").. the DbContext with the connection-string I just built up.
........
So while your question appears on the surface to be "trivial".. you are entering the world of "how do I keep my secrets secure".
It isn't impossible. But it is NOT "not complicated" (<<double negative) either. Aka, it is going to be a little complicated.
Security (real security) is hard(er) then easy peezey.
..
Finally, here is a link:
https://stackoverflow.com/a/62431998/214977
You do NOT do "config setting reads" in dot-net-CORE like you might have done in dot-net-FRAMEWORK.
You do not (in Core).. do a "read a value from app.config/web.config on the fly".
You do INJECTION...even for config-values... see the SOF link for more info.
Here is how I use
/* set the initial value .. that does NOT have the password */
SqlConnectionStringBuilder builder =
new SqlConnectionStringBuilder("server=(local);user id=ab;initial catalog=AdventureWorks");
// now read "just the password" from a real secret store/retriever
string justThePassword = /**/
builder.Password = justThePassword;
string entireConnectionStringWithPassword = builder.ConnectionString;
/* now use entireConnectionStringWithPassword to wire up my custom db context */
/* see https://learn.microsoft.com/en-us/dotnet/api/system.data.sqlclient.sqlconnectionstringbuilder?view=dotnet-plat-ext-7.0 */
10 Comments
ISecretsReader is completely unnecessary when interfacing with Azure KeyVault using standard means, which is via Azure AppConfiguration leveraging the existing Extensions.Configuration mechanism with Azure KeyVault references.Connection String for this You can Encrypt & Decrypt.
You can update the ConnectionStrings field in appsettings.json by Encrypting it as follows.
Step 1:
public static class StringCipher
{
private const string initVector = "B3hC2V0YVMf6OvLW";
private const int keysize = 256;
private const string key = "CRY";
public static string EncryptString(string text)
{
var textBytes = Encoding.UTF8.GetBytes(text);
var symmetricKey = new RijndaelManaged()
{
Padding = PaddingMode.ANSIX923,
Mode = CipherMode.ECB
};
var keyBytes = new PasswordDeriveBytes(key, null).GetBytes(keysize / 8);
var encryptor = symmetricKey.CreateEncryptor(keyBytes, Encoding.UTF8.GetBytes(initVector));
var memoryStream = new MemoryStream();
var cryptoStream = new CryptoStream(memoryStream, encryptor, CryptoStreamMode.Write);
cryptoStream.Write(textBytes, 0, textBytes.Length);
cryptoStream.FlushFinalBlock();
var cipherTextBytes = memoryStream.ToArray();
memoryStream.Close();
cryptoStream.Close();
return Convert.ToBase64String(cipherTextBytes);
}
public static string DecryptString(string cipherText)
{
var cipherTextBytes = Convert.FromBase64String(cipherText);
var keyBytes = new PasswordDeriveBytes(key, null).GetBytes(keysize / 8);
var symmetricKey = new RijndaelManaged
{
Padding = PaddingMode.ANSIX923,
Mode = CipherMode.ECB
};
var decryptor = symmetricKey.CreateDecryptor(keyBytes, Encoding.UTF8.GetBytes(initVector));
var memoryStream = new MemoryStream(cipherTextBytes);
var cryptoStream = new CryptoStream(memoryStream, decryptor, CryptoStreamMode.Read);
var plainTextBytes = new byte[cipherTextBytes.Length];
var decryptedByteCount = cryptoStream.Read(plainTextBytes, 0, plainTextBytes.Length);
memoryStream.Close();
cryptoStream.Close();
return Encoding.UTF8.GetString(plainTextBytes, 0, decryptedByteCount);
}
}
Step 2:
public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
services.AddDbContext<ApplicationDbContext>(options =>
options.UseSqlServer(StringCipher.DecryptString(Configuration.GetConnectionString("DefaultConnection")));
}
}
2 Comments
DecryptString method would help anyone... You've also glossed over how to encrypt the connection string in the first place.