Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Proper Storage of Configuration Strings

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.

like image 316
Missy Avatar asked Apr 10 '17 00:04

Missy


People also ask

How are connection strings stored in a configuration file?

Connection strings can be stored as key/value pairs in the connectionStrings section of the configuration element of an application configuration file. Child elements include add, clear, and remove. The following configuration file fragment demonstrates the schema and syntax for storing a connection string.

What is the best way to store configuration files?

Instead of storing the whole configuration in one file, store it in several files. Have a configuration directory. All files there are interpreted as configuration files, except maybe README*. All file names are sorted alphabetically, and the files are loaded in that order.

What are the valid values for the default string storage architecture?

Valid values for this property include the following: Specifies the default string storage architecture, subject to a 4 GB maximum file size per store. Specifies larger string storage, supports up to 4 billion unique strings per store.

How to store config files with secret values in VCS?

Only store in the VCS the config files with safe-to-see values, or default values. Add the config files with secrets during deployment, or, better yet, use an authenticated secrets storage service. On your nearest Linux box, take a look at /etc/sudoers.d or /etc/nginx/conf.d. It shows the same pattern.


2 Answers

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

like image 176
Gregor Primar Avatar answered Sep 21 '22 13:09

Gregor Primar


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.

like image 45
Chamika Goonetilaka Avatar answered Sep 19 '22 13:09

Chamika Goonetilaka