Where to store db passwords when using Windows .NET or ASP.NET applications
Asked Answered
S

5

13

I have a scenario that has been troubling me for years. If you have to connect to a database or other service (like a web service) using a username and password, where would be the safest place to store this information if you are connecting through a .NET assembly? I understand that you would have to encrypt the password, but then you run into a kind of chicken-egg problem -- fine -- you can encrypt it, but then where do you put the key?

In .NET, you can't hard-code the password because you can decompile .NET code.

I looked at using assembly based rights with Isolated Storage, but MS recommends against storing unencrypted secret items there because privileged users can gain access, so again, we are moving the problem from point A to point B. So, for example, a domain admin with no need to know about the information in a database would be able to get access because of the ability to be an admin on any workstation on the domain.

You can encrypt the App. Config and Web.Config, but I believe privileged users can access the keys.

I think you run into the same problem with DPAPI.

I had considered storing the passwords, encrypted in a remote database, and getting them through OS authentication, but our department prohibits the storage of passwords on database servers. I am pretty sure I am stuck and wanted confirmation.

Skewbald answered 7/3, 2012 at 22:4 Comment(1)
I know it is a while ago, but I've provided an answer to your question how to store passwords encrypted. Please have a look.Unintelligent
D
11

You don't want to store the password in the assembly, and reinventing the wheel only creates more trouble (and introduces more vulnerabilities) than it's worth. If you are using MS platform on both the database and web server, then the easiest way to handle this is use a trusted connection, and grant rights on the SQL server to the identity your application is using.

Second to that, I would just let DPAPI do its job to encrypt your connection settings.

Duration answered 7/3, 2012 at 22:8 Comment(4)
The major problem I've run into is that both the web and Windows app I am working with run where I do not have the ability to use OS authentication. The windows app in particular is on a multi user box -- as far as I can tell from the docs, I would have to config seperate settings for each user if I use DPAPI in user mode. If I use DPAPI in machine mode, my connection information will be available to all apps using DPAPI. Unfortunately, I also have to use FIPS 140 Compliant providers, which I think rules DPAPI out as well as RSA. I think the best way to do this is get the password off the box.Skewbald
Ooops. DPAPI used TripleDES, which I do believe is FIPS. This still doesn't help in the multi user access area where in machine mode, all apps using DPAPI (as I understand it) have access to the same informationSkewbald
It is possible to encrypt config sections using user-store DPAPI. I've updated my answer with a link with instructions. Note that the instructions lack any guidance on configuring your app pool (if using IIS7+) to load the identity's user profile. I think you'll need to switch that flag on, since DPAPI user-store requires the profile to be loaded.Duration
The thing is, if you move passwords off the box, you still need to secure the password to get to the passwords. Also, this makes the infrastructure in getting credentials that much more complicated, and introduces a little bit of frailty. I'm curious as to what the problem would be in regards to user-store DPAPI. Sounds like you could really use x509 or identity management in your applications!Duration
U
5

If you just want to keep it away from GIT, and don't require encryption, you can use the Secret Manager Tool as described. Then it is stored in a secrets.json file within your user profile (but unencrypted). And as Microsoft describes it, it is limited to ASP.NET Core applications, which is not true - another StackOverflow Q&A shows you how to do it in a .NET core console application as well.

But if you need  encryption,  you can use the following methods of the .NET Framework; they use the DPAPI internally to protect (encrypt) your data, and you can directly use them in C# or VB.NET without having to fiddle around with system DLL calls:

namespace System.Security.Cryptography
{
    // Summary:
    //     Provides methods for protecting and unprotecting data. This class cannot
    //     be inherited.
    public sealed class ProtectedData
    {
        public static byte[] Protect(byte[] userData, 
            byte[] optionalEntropy, DataProtectionScope scope);
        public static byte[] Unprotect(byte[] encryptedData, 
            byte[] optionalEntropy, DataProtectionScope scope);
    }
}

To use it, add the reference System.Security to your project. I strongly recommend using the byte array optionalEntropy to add a SALT to your protected data (add some random values to the byte array which are unique for the data you intend to protect).

For scope you can use DataProtectionScope.CurrentUser, which will encrypt the data to protect with the current user's credentials.

In some scenarios, DataProtectionScope.LocalMachine is useful, as well. In this case, the protected data is associated with the machine context. With this setting, any process running on the computer can unprotect data. It is usually used in server-specific applications that run on a server where untrusted users are not allowed access.

Use the Protect method to encrypt the data, decrypt it with Unprotect. You may store the returned byte array according to the requirements of your application (file, database, registry, etc).

If you have to protect a string, you can use a helper method such as:

byte[] GetBytes(string text) => System.Text.Encoding.Unicode.GetBytes(text);

to convert it to a byte array.

Vice versa, if you unprotect the byte array, you can get the string back vie:

string GetString(byte[] text) => System.Text.Encoding.Unicode.GetString(text);

If your string is not Unicode, you can find more encodings in the System.Text.Encoding library of .NET.

Simple example (using the helper methods above together with the ProtectedData class):

using System.Security.Cryptography;
using static System.Security.Cryptography.ProtectedData;

void Main()
{
    const DataProtectionScope scope = DataProtectionScope.CurrentUser;
    byte[] salt = GetBytes("s5Dk9Fj2L12"); // change the salt
    
    // protect msg
    string msg = "Hello World!";
    var protMsg = Protect(GetBytes(msg), salt, scope);
    
    // get plaintext back
    string plainTxt = GetString(Unprotect(protMsg, salt, scope));
    Console.WriteLine(plainTxt);
}

More about the methods from DPAPI can be found here at MSDN:

For code samples and in case you are interested in encrypting parts of the application's .config file, check this out:

I recommend you to use a SALT (by using the optionalEntropy parameter) - it protects against rainbow table attacks.


There is one drawback of the DPAPI solution I'd like to mention: the key is generated based on your Windows credentials, which means whoever has access to your Windows credentials possibly has access to the protected data (but note that the same is true if you're using BitLocker to encrypt your files). A program running under your account can access the protected data as well.


Finally one remark: You can combine the techniques described here (i.e. use the Secret Manager Tool and encrypt the settings stored in the secret.json file - however then you require to store it in text format so you might need to encrypt and then encode it with Base64).

Unintelligent answered 11/3, 2013 at 15:19 Comment(0)
H
1

This is good question and I've been looking for an answer myself. Problem I had was to keep db passwords secure in case server was hacked and individual files could be retrieved. One very interesting option I've found was that sections of web.config can be encrypted and decrypted automatically on the fly by .NET framework which would use Windows secure store to keep and retrieve encryption key for you. In my situation that was not available because my hosting provider was not supporting it but you may have a look at this option. Why I think it may work is that you can independently manage security of what users may access Windows secure store and significantly limit any potential breaches. A hacker who breaks into server might get a copy of your config files and all your assemblies but accessing decryption key would be another obstacle for him.

Heifetz answered 8/3, 2012 at 22:2 Comment(0)
D
1

****UPDATED for Windows 10 1903+ ****

Microsoft has removed the dlls used by the previous method from WinMetadata and they are now not available even when doing the old manipulations. Instead, they now either propose to use a Target Framework Moniker or a Nuget package called Microsoft.Windows.SDK.Contracts that expose the modern windows APIs. [The official detail could be found here]. Note that for the nuget to install correctly, it is essential that the packet management is set to PackageReference instead of Packages.config elsewise, even though it could look like it is installed, it won't work.

If it is not already set to this manager, it is normally possible to convert the packages.config file to the new format (that is integrated in the project file) by right-clicking the packages.config file and choosing Migrate. If the Packages.config file does not yet exist, it is possible to set the default to use PackageReference by going in the Nuget Options. Note that the default setting varies between .Net FW, .Net Std and .Net Core. When I personally had to do it, weirdly, it didn't want to migrate the file. I had to completely uninstall my packages, delete the file, set the default and reinstall the packages. Something to do with TFVC if I remember well.

The APIs should then be available again.

**** ORIGINAL POST ****

If you are in a Windows only 8+ solution, you could also use the Windows Password Vault. Originally, this was built for Metro Apps but is also supported for Winform and WPF applications.

Basically, what you need is

  1. Add the following line in your project file inside the first property group
    <TargetPlatformVersion>8.0</TargetPlatformVersion>

  2. Reference Windows.Security.

    • Open the References Manager window
    • Select Windows tab
    • Select Core sub tab
    • Check Windows.Security.
  3. In the code, use (this is vb but C# is equivalent)

    Dim vault = New Windows.Security.Credentials.PasswordVault()
    vault.Add(New Windows.Security.Credentials.PasswordCredential(resource, userName, password))
    Dim cred = vault.Retrieve(resource, logon)
    cred.RetrievePassword()
    Dim pwd = cred.Password
    

Ref:

  1. https://learn.microsoft.com/fr-ca/archive/blogs/cdndevs/using-windows-8-winrt-apis-in-net-desktop-applications
  2. https://learn.microsoft.com/en-us/uwp/api/Windows.Security.Credentials.PasswordVault?view=winrt-19041
Deberadeberry answered 25/11, 2020 at 20:6 Comment(0)
C
0

There are a couple of options here.

  1. Store them in the config file encrypted
  2. Store them in an external file that is encrypted with a generated seed. Obfuscate the code that stores this base seed or store it in a c++ dll (harded to decompile).
Cronus answered 8/3, 2012 at 2:47 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.