1

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?

8
  • Do you mean connection-string as a db-connection string? Are you using EntityFrame-Core? Are you using "AddDbContext" (with a MyCustomAppDbContext)? Commented 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.? Commented 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 protected Commented 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. Commented 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). Commented Jul 26, 2023 at 6:53

2 Answers 2

-1

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

https://learn.microsoft.com/en-us/dotnet/api/system.data.sqlclient.sqlconnectionstringbuilder?view=dotnet-plat-ext-7.0

....

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:

https://medium.com/@nayanajith89/connect-app-service-with-azure-key-vault-without-exposing-key-vault-password-d3abf55f7ef9

...

And so I use

https://learn.microsoft.com/en-us/dotnet/api/microsoft.aspnetcore.hosting.hostingenvironmentextensions.isdevelopment?view=aspnetcore-7.0

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 */
Sign up to request clarification or add additional context in comments.

10 Comments

Maybe you want to summarize this answer so it becomes shorter and easier for the user to connect the pieces in the puzzle
Please don't recommend extra abstractions to people... that 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.
I gave a reason for the abstraction besides "just add it because". I differentiated between the local-developer-experience and deployment-code-experience. code-maze.com/dependency-inversion-principle
Thank you sir. What an answer. I have learned something from it for sure. Now I will try to implement some of it to my own code. Once again thank you.
You are reinventing a worst wheel @granadaCoder. The reasoning is bad. There is already a standard mechanism to read key vault secrets into your application's configuration using the Microsoft implementation. All you are doing is introducing complexity and making the life of every single developer that needs to maintain this harder. I'd strongly suggest taking a look at KeyVault references and then updating your answer accordingly to remove your unnecessary abstraction.
|
-2

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

I don't see how that DecryptString method would help anyone... You've also glossed over how to encrypt the connection string in the first place.
After the ConnectionString is manually encrypted, appsettings.json is updated. By Decrypting the encrypted ConnectionString. I hope this helped.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.