6

I am using Visual Studio and I am very confused about the best way to store configuration strings. I am creating a Windows Forms Application. I need very basic security -- I don't want the password to be readable in app.config but I am not concerned about someone disassembling my code in order to figure it out.

So, in the Data Source Wizard, I said "Don't Save Password" and then I put the following code in Settings.Designer.CS:

public string MyConnectionString {
        get {
            return ((string)("Data Source=SQLSERVER\\ACCOUNTING;Initial Catalog=ACCOUNTING;User ID=MyUser;Password=28947239SKJFKJF"));
        }
    }

I realize that this isn't the best solution but I can't think of a better one. I would appreciate anyone's help and input on this.

Thanks --

Missy.

Missy
  • 1,286
  • 23
  • 52
  • 1
    First consider whether windows security is appropriate. – Nick.Mc Apr 10 '17 at 01:04
  • 2
    [Secure Connection Strings](https://msdn.microsoft.com/en-us/library/dx0f3cf2(v=vs.85).aspx) – Lei Yang Apr 10 '17 at 01:18
  • Windows security is not appropriate in this specific scenario. – Missy Apr 10 '17 at 01:22
  • I believe the aspnet_regiis tool is specific to the computer the system is running on. Also, I should have been specific in that this is a Windows Forms App. – Missy Apr 10 '17 at 01:25
  • Since security is not a huge concern, you can use code like this one: http://stackoverflow.com/questions/10168240/encrypting-decrypting-a-string-in-c-sharp and just replace your connection string by the encrypted one, and hardcode the passphrase in your code directly. – Simon Mourier Apr 12 '17 at 22:16
  • 1
    Please don't cast a string to a string – Buh Buh Apr 13 '17 at 13:31
  • A good best practice is to add a custom user and use integrated security to connect to your sql server with that user on your machine; although, I am not sure how this works now in .NET Core. – cr1pto May 03 '20 at 05:16

5 Answers5

8

You can use RsaProtectedConfigurationProvider to encrypt your ConnectionStrings Section. Here is short sample how to encrypt and decrypt this section (Just take note, start your Visual Studio as Administrator):

Primary web config:

<?xml version="1.0"?>
<configuration>
  <startup>
    <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.0"/>
  </startup>

  <connectionStrings>
    <add name="MyConnKey" connectionString="Data Source=SQLSERVER\\ACCOUNTING;Initial Catalog=ACCOUNTING;User ID=MyUser;Password=28947239SKJFKJF" />
  </connectionStrings>

  <appSettings>
    <add key="DD" value="567_Access"/>
  </appSettings>

</configuration>

Code:

static void Main(string[] args)
{
    Configuration config = ConfigurationManager.OpenExeConfiguration(Application.ExecutablePath);
    ConfigurationSection section = config.GetSection("connectionStrings") as ConnectionStringsSection;
    if (!section.SectionInformation.IsProtected)
    {
        Console.WriteLine("Protecting connection strings...");
        section.SectionInformation.ProtectSection("RsaProtectedConfigurationProvider");
    }
    else
    {
        Console.WriteLine("Unprotecting connection strings...");
        section.SectionInformation.UnprotectSection();
    }
    section.SectionInformation.ForceSave = true;
    config.Save(ConfigurationSaveMode.Full);

    var cs = System.Configuration.ConfigurationManager.ConnectionStrings["MyConnKey"];
    Console.WriteLine(cs.ConnectionString);

    Console.ReadLine();
}

And here is how coded config looks like:

<?xml version="1.0"?>
<configuration>
  <startup>
    <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.0"/>
  </startup>

  <connectionStrings configProtectionProvider="RsaProtectedConfigurationProvider">
    <EncryptedData Type="http://www.w3.org/2001/04/xmlenc#Element"
      xmlns="http://www.w3.org/2001/04/xmlenc#">
      <EncryptionMethod Algorithm="http://www.w3.org/2001/04/xmlenc#tripledes-cbc" />
      <KeyInfo xmlns="http://www.w3.org/2000/09/xmldsig#">
        <EncryptedKey xmlns="http://www.w3.org/2001/04/xmlenc#">
          <EncryptionMethod Algorithm="http://www.w3.org/2001/04/xmlenc#rsa-1_5" />
          <KeyInfo xmlns="http://www.w3.org/2000/09/xmldsig#">
            <KeyName>Rsa Key</KeyName>
          </KeyInfo>
          <CipherData>
            <CipherValue>js82TLzdIfcdD51g2Us8Nv2eWTSval7oi2Xl+OJsL2c2hUDrm21YG/v1yhuB5Ag8/Uubm9gjmQYcPImo8VOXXDZxEW/HIYNbbkDsopbAyyXNGkHtTrEqz80nqAyipn+Y5QpwXKxFJoaEMPaPdO5juXYd2SPdGaFMBg4m2+drSy6bvXnloz+GIXKbL9QNdxg8br1S8ALUxXsu4F52sKda6J/Sk+I9SBf85XK/JKaHQFoHghf1/m58Zh0hIhci3R6wwGDC3mVG/NcL3tWKpga3ndQ+57FBezsWWOMKyLFPMZG7NkNvBaNG0fYJm2+ApKme1gGil2GGivxySP4evL4hRw==</CipherValue>
          </CipherData>
        </EncryptedKey>
      </KeyInfo>
      <CipherData>
        <CipherValue>r28s2mSZTEwb99SIJH7kozBR8RkY8LkxzVLm/VExEwc3aLqSJgqJGrNY6f4mtvCT9ZIYV/QjErt9weQNYSZxyou4RXAq1W8yYnzv+7NCvgOgKvAQh/p+iQidh13SmnC6UCtSrMp3HeRSFNj1y1sF2TGYVpWqWA9NAEBsOyYr0ey6S6/fUFrLAy7mcCkawemmDRvxqF7YnG+LoL9Bh59/l++BhTYlMQvz/stHb5mA6bfKgZYbYDA9KEr5mdrr9t8GGzrk3vNW5s723bKZuiqUWiZfWklY2a2NuONDKj4FG3cAUwCdXq2OzIBFVnXSBgrMo+4GCgQar7delms+bvFOnjozrHdKHJLoahithwPEmDuiM4SJJAZHXKTpFrmv4o+YT68i9xs0iUy0p/hnb5lJv3ITCmEnsOmewn7xIsoPcZMEK54kAtoyjXt2H3QR7KdI3Sf4R6X3rHYr4BerF0UatdP8q5ppLi6uYT/epDi0qTFgf9aDuOW2zDc1TYzFEhBrg4sr2DqqTJWgpyI0yVcq76ZmSTwa53ReyvHFuLkMadijJuUe+u5zPj0BDR6kl8vXN0OzXjby8Uw=</CipherValue>
      </CipherData>
    </EncryptedData>
  </connectionStrings>

  <appSettings>
    <add key="DD" value="567_Access"/>
  </appSettings>

</configuration>

Take notice that in this case decrypt will work only on machine where encrypt was processed in first place. For more info please visit RsaProtectedConfigurationProvider

Gregor Primar
  • 6,759
  • 2
  • 33
  • 46
  • Thank you so much for your long and thoughtful answer. Sadly, I have already evaluated this option and it will not work for me. – Missy Apr 12 '17 at 23:37
  • @Missy Can you elaborate on why this would not work? There are not a lot of other secure options to store a password. Using SSPI for the authentication is the best option, but this is 2nd best and likely the only way you'll pass a security scan. –  Apr 18 '17 at 02:05
  • @Gregor Thank you for taking time to give a proper answer. I hope others that view this page looking for advice understand that your answer is the best approach. –  Apr 19 '17 at 23:00
3

You may use the standard Rijndael algorithm to encrypt the entire connection string. You only need to keep the algorithm's password and the salt values in the code level (you may consider them as application constants).

App Config:-

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <connectionStrings>
    <add name="DbContext" connectionString="7ryM3BFhWTwVGpeMWK0pMMujIwj7j+GvrJf7xewEW4Pd+uq0W8aSq85eaEp6+O2Gom98iVNHcyeuaG/93B2y/uJKyHmSnsBlHT3UtBpnT8Lx3OragLK5EXtIiVl38uq10bMga055qq1dACR6XQQeIQ==" providerName="System.Data.SqlClient" />
  </connectionStrings>
</configuration>

Code:-

class Program
{
    private static string _password = "0B6854E7-20AA-4B0E-978A-410152AA1B41";

    static void Main(string[] args)
    {
        var connection = System.Configuration.ConfigurationManager.ConnectionStrings["DbContext"].ConnectionString;
        var salt = "Pa$$w0rd";
        var plainConnection = DecryptRijndael(connection, salt);

        //var encrypted = EncryptRijndael(connection, salt);

    }

    public static string EncryptRijndael(string text, string salt)
    {
        if (string.IsNullOrEmpty(text))
            throw new ArgumentNullException("text");

        using(var aesAlg = NewRijndaelManaged(salt))
        using(var encryptor = aesAlg.CreateEncryptor(aesAlg.Key, aesAlg.IV))
        using (var msEncrypt = new MemoryStream())
        {
            using (var csEncrypt = new CryptoStream(msEncrypt, encryptor, CryptoStreamMode.Write))
            using (var swEncrypt = new StreamWriter(csEncrypt))
                swEncrypt.Write(text);

            return Convert.ToBase64String(msEncrypt.ToArray());
        }
    }

    public static string DecryptRijndael(string cipherText, string salt)
    {
        if (string.IsNullOrEmpty(cipherText))
            throw new ArgumentNullException("cipherText");

        if (!IsBase64String(cipherText))
            throw new Exception("The cipherText input parameter is not base64 encoded");

        using (var aesAlg = NewRijndaelManaged(salt))
        using (var decryptor = aesAlg.CreateDecryptor(aesAlg.Key, aesAlg.IV))
        {
            var cipher = Convert.FromBase64String(cipherText);

            using (var msDecrypt = new MemoryStream(cipher))
            using (var csDecrypt = new CryptoStream(msDecrypt, decryptor, CryptoStreamMode.Read))
            using (var srDecrypt = new StreamReader(csDecrypt))
                return srDecrypt.ReadToEnd();
        }
    }

    private static bool IsBase64String(string base64String)
    {
        base64String = base64String.Trim();
        return (base64String.Length % 4 == 0) &&
               Regex.IsMatch(base64String, @"^[a-zA-Z0-9\+/]*={0,3}$", RegexOptions.None);
    }

    private static RijndaelManaged NewRijndaelManaged(string salt)
    {
        if (salt == null) throw new ArgumentNullException("salt");
        var saltBytes = Encoding.ASCII.GetBytes(salt);
        using (var key = new Rfc2898DeriveBytes(_password, saltBytes))
        {
            var aesAlg = new RijndaelManaged();
            aesAlg.Key = key.GetBytes(aesAlg.KeySize / 8);
            aesAlg.IV = key.GetBytes(aesAlg.BlockSize / 8);

            return aesAlg;
        }
    }
}

Hope this helps.

  • 1
    Embedding password/crypto key in code is a bad practice. –  Apr 18 '17 at 02:02
  • @Nathan All depends on the requirements. Considering the requirements and the limitations of the user, this is a one viable solution. – Chamika Goonetilaka Apr 19 '17 at 06:01
  • It is bad advice. This is usually the normal result of copying code from Code Project. I predict someone will spend some late nights trying to meet a release deadline because they failed a security audit a few days before code freeze. (You should probably also attribute your answer to the source.) https://www.codeproject.com/Tips/704372/How-to-Use-Rijndael-ManagedEncryption-with-Csharp –  Apr 19 '17 at 22:57
2

If you want to have unreadable connection string or specific string then put it in registry..

In App.config:

<appSettings>
    <add key="dbname" value="dbname"/>
    <add key="username" value="uname"/>
    ..... and so on
</appSettings>

Then to your code behind:

 string dbname = ConfigurationManager.AppSettings["dbname"].ToString();

Then make a full connection here:

String yourConnectionString = "DataSorce="DataSourceFromConfig  + " " + Password from registry or password from appconfig that encrypted + "" + and so on:

It's all depend on you..

Vijunav Vastivch
  • 4,153
  • 1
  • 16
  • 30
  • This is a good thought but my security team won't let me use the registry. – Missy Apr 10 '17 at 14:54
  • That's silly.. If someone gets access to your App.config, they can probably get a hold of anything in your code as well. – Sava B. Apr 12 '17 at 16:17
2

you could develop a separate tool. This tool take your connection string as input and returns a encrypted string. You must use algorithm for encrypting the string.

After getting encrypted connection string you put it into the config file. When you access the database in the program. You need to decrypt the connection string with same salt password which is use for encrypting the string.

1

As far as I can tell, the best way to do this is to do what @Gregor Primar suggests.

You can also try what this person has suggested:

https://stackoverflow.com/a/619305/5596684

Encrypt: aspnet_regiis -pef "connectionStrings" "c:\folder\"

Decrypt: aspnet_regiis -pdf "connectionStrings" "c:\folder\"

The bottom line is encrypting your string and decrypting it is the best approach if you are concerned about someone seeing your connection string settings.

Follow Microsoft's best practices here:

https://msdn.microsoft.com/en-us/library/89211k9b(v=vs.110).aspx

Community
  • 1
  • 1
cr1pto
  • 539
  • 3
  • 13