存储SQL连接详细信息 - 如何安全地加密每个用户?

时间:2021-05-25 06:51:59

I have an application that connects to a number of SQL Servers for monitoring and other tasks. Currently, I only support trusted authentication, since I don't have to store anything sensitive. I want to add the ability to use SQL Authentication (username/password).

我有一个应用程序连接到许多SQL Server进行监视和其他任务。目前,我只支持受信任的身份验证,因为我不需要存储任何敏感的内容。我想添加使用SQL身份验证(用户名/密码)的功能。

What's the best approach to storing this sensitive data between sessions? Is there a user-only certificate or encryption key available to me that I can use? Is it safe enough to use a registry key that's generated randomly per-user to encrypt this information? If there's a way I can use a key (or create one and store it) in such a way that no other user on the computer can access it, that's ideal.

在会话之间存储这些敏感数据的最佳方法是什么?是否有我可以使用的用户专用证书或加密密钥?是否足够安全使用每个用户随机生成的注册表项来加密此信息?如果有一种方法我可以使用一个键(或创建一个并存储它),使计算机上没有其他用户可以访问它,这是理想的。

I understand encryption, so I'm not looking for a tutorial - I'm looking for the safest method to keep one user's configuration data for my application safe from other users.

我理解加密,所以我不是在寻找一个教程 - 我正在寻找最安全的方法来保护我的应用程序的一个用户的配置数据免受其他用户的影响。

3 个解决方案

#1


Use Data Protection API (aka. DPAPI). Each use has a key that is protected by its password. You store the password/connection string in a the .config file and .Net framework has methods to encrypt/decrypt a config section with a machine key, an user key or an RSA key. Don't reinvent the wheel doing your own custom scheme. And using a registry key is definitely a bad idea. Security comes from secrets, not from access protecttion: it must rely on the user providing a secret (its password at login time), not on a unnaccessible registry key.

使用Data Protection API(又名DPAPI)。每次使用都有一个受密码保护的密钥。将密码/连接字符串存储在.config文件中,.Net框架具有使用机器密钥,用户密钥或RSA密钥加密/解密配置节的方法。不要重新发明*做自己的自定义方案。使用注册表项肯定是个坏主意。安全来自秘密,而不是来自访问保护:它必须依赖于提供秘密的用户(在登录时提供密码),而不是依赖于无法访问的注册表密钥。

How To: Encrypt Configuration Sections in ASP.NET 2.0 Using DPAPI

如何:使用DPAPI加密ASP.NET 2.0中的配置节

#2


As Remus said, use DPAPI. But instead of using PInvoke methods (as in your linked example), use the ProtectedData class. That is a managed wrapper around DPAPI. A lot of the examples use PInvoke to access DPAPI because there was no managed way to do it before .Net 2.0. The DataProtectionScope class enables you to encrypt/decrypt data for the current user or machine.

正如Remus所说,使用DPAPI。但是,不要使用PInvoke方法(如在链接示例中),而是使用ProtectedData类。这是围绕DPAPI的托管包装器。许多示例使用PInvoke来访问DPAPI,因为在.Net 2.0之前没有可管理的方法来访问它。 DataProtectionScope类使您可以加密/解密当前用户或计算机的数据。

#3


As Remus pointed out, there is user/machine level encryption available.

正如Remus指出的那样,可以使用用户/机器级加密。

i'm generally terrified of such things because that data can be lost relativly easily.

我一般都对这些事情感到害怕,因为这些数据很容易相对丢失。

If i were you i'd roll my own system. Encrypt the connection strings using a key hard-coded in your application, convert to Base64, and store the resulting, encrypted, connection strings in the registry.

如果我是你,我会推出自己的系统。使用应用程序中硬编码的密钥加密连接字符串,转换为Base64,并将结果加密的连接字符串存储在注册表中。


The following function takes a string, encrypts it with AES-256 and the specified key, and base64's the result so it's back as a printable string:

以下函数接受一个字符串,使用AES-256和指定的密钥对其进行加密,然后使用base64作为可打印的字符串:

Sample Usage:

String connectionString = EncryptString(
     "Provider=SQLOLEDB;Data Source=Lithium;User Id=sa;Password=hello",
     "A fairly complicated password, like a guid: 8B4B0D73-84C9-4A1E-8DD2-9A189F84FD9B");


public static string EncryptString(string source, string key)
{
    Rfc2898DeriveBytes deriveBytes = new Rfc2898DeriveBytes(key, salt);
    byte[] derivedKey = deriveBytes.GetBytes(derivedKeySize);


    Rijndael rijndael = Rijndael.Create();

    rijndael.Mode = cipherMode;
    rijndael.Padding = paddingMode;
    rijndael.KeySize = keySize;
    rijndael.BlockSize = blockSize;
    rijndael.FeedbackSize = blockSize; // no bigger than the blocksize

    rijndael.Key = derivedKey;
    rijndael.IV = iv;

    ICryptoTransform transform = rijndael.CreateEncryptor();

    byte[] encoded = Encoding.UTF8.GetBytes(source);

    byte[] target = transform.TransformFinalBlock(encoded, 0, encoded.Length);
    return Convert.ToBase64String(target);
}

public static string DecryptString(string source, string key)
{
    Rfc2898DeriveBytes deriveBytes = new Rfc2898DeriveBytes(key, salt);
    byte[] derivedKey = deriveBytes.GetBytes(derivedKeySize);

    Rijndael rijndael = Rijndael.Create();

    rijndael.Mode = cipherMode;
    rijndael.Padding = paddingMode;
    rijndael.KeySize = keySize;
    rijndael.BlockSize = blockSize;
    rijndael.FeedbackSize = blockSize; // no bigger than the blocksize

    rijndael.Key = derivedKey;
    rijndael.IV = iv;

    ICryptoTransform transform = rijndael.CreateDecryptor();

    byte[] decoded = Convert.FromBase64String(source);
    byte[] target = transform.TransformFinalBlock(decoded, 0, decoded.Length);

    return Encoding.UTF8.GetString(target);
}

    private static readonly byte[] iv = {
        0x30,0xA6,0x65,0xDE,0x8C,0x63,0x17,0x44,
        0xB6,0xFD,0xEA,0x5F,0x76,0xA1,0x1C,0x5F
    };

    private static readonly byte[] salt = {
        0xF9,0x39,0x0C,0xE0,0x22,0xE0,0x8E,0x84,
        0xB2,0x05,0x1E,0xA8,0x6D,0x1C,0x39,0xAC
    };

    private const int keySize = 256;
    private const int blockSize = 128;
    private const CipherMode cipherMode = CipherMode.CBC;
    private const PaddingMode paddingMode = PaddingMode.PKCS7;
    private const int derivedKeySize = 32;

#1


Use Data Protection API (aka. DPAPI). Each use has a key that is protected by its password. You store the password/connection string in a the .config file and .Net framework has methods to encrypt/decrypt a config section with a machine key, an user key or an RSA key. Don't reinvent the wheel doing your own custom scheme. And using a registry key is definitely a bad idea. Security comes from secrets, not from access protecttion: it must rely on the user providing a secret (its password at login time), not on a unnaccessible registry key.

使用Data Protection API(又名DPAPI)。每次使用都有一个受密码保护的密钥。将密码/连接字符串存储在.config文件中,.Net框架具有使用机器密钥,用户密钥或RSA密钥加密/解密配置节的方法。不要重新发明*做自己的自定义方案。使用注册表项肯定是个坏主意。安全来自秘密,而不是来自访问保护:它必须依赖于提供秘密的用户(在登录时提供密码),而不是依赖于无法访问的注册表密钥。

How To: Encrypt Configuration Sections in ASP.NET 2.0 Using DPAPI

如何:使用DPAPI加密ASP.NET 2.0中的配置节

#2


As Remus said, use DPAPI. But instead of using PInvoke methods (as in your linked example), use the ProtectedData class. That is a managed wrapper around DPAPI. A lot of the examples use PInvoke to access DPAPI because there was no managed way to do it before .Net 2.0. The DataProtectionScope class enables you to encrypt/decrypt data for the current user or machine.

正如Remus所说,使用DPAPI。但是,不要使用PInvoke方法(如在链接示例中),而是使用ProtectedData类。这是围绕DPAPI的托管包装器。许多示例使用PInvoke来访问DPAPI,因为在.Net 2.0之前没有可管理的方法来访问它。 DataProtectionScope类使您可以加密/解密当前用户或计算机的数据。

#3


As Remus pointed out, there is user/machine level encryption available.

正如Remus指出的那样,可以使用用户/机器级加密。

i'm generally terrified of such things because that data can be lost relativly easily.

我一般都对这些事情感到害怕,因为这些数据很容易相对丢失。

If i were you i'd roll my own system. Encrypt the connection strings using a key hard-coded in your application, convert to Base64, and store the resulting, encrypted, connection strings in the registry.

如果我是你,我会推出自己的系统。使用应用程序中硬编码的密钥加密连接字符串,转换为Base64,并将结果加密的连接字符串存储在注册表中。


The following function takes a string, encrypts it with AES-256 and the specified key, and base64's the result so it's back as a printable string:

以下函数接受一个字符串,使用AES-256和指定的密钥对其进行加密,然后使用base64作为可打印的字符串:

Sample Usage:

String connectionString = EncryptString(
     "Provider=SQLOLEDB;Data Source=Lithium;User Id=sa;Password=hello",
     "A fairly complicated password, like a guid: 8B4B0D73-84C9-4A1E-8DD2-9A189F84FD9B");


public static string EncryptString(string source, string key)
{
    Rfc2898DeriveBytes deriveBytes = new Rfc2898DeriveBytes(key, salt);
    byte[] derivedKey = deriveBytes.GetBytes(derivedKeySize);


    Rijndael rijndael = Rijndael.Create();

    rijndael.Mode = cipherMode;
    rijndael.Padding = paddingMode;
    rijndael.KeySize = keySize;
    rijndael.BlockSize = blockSize;
    rijndael.FeedbackSize = blockSize; // no bigger than the blocksize

    rijndael.Key = derivedKey;
    rijndael.IV = iv;

    ICryptoTransform transform = rijndael.CreateEncryptor();

    byte[] encoded = Encoding.UTF8.GetBytes(source);

    byte[] target = transform.TransformFinalBlock(encoded, 0, encoded.Length);
    return Convert.ToBase64String(target);
}

public static string DecryptString(string source, string key)
{
    Rfc2898DeriveBytes deriveBytes = new Rfc2898DeriveBytes(key, salt);
    byte[] derivedKey = deriveBytes.GetBytes(derivedKeySize);

    Rijndael rijndael = Rijndael.Create();

    rijndael.Mode = cipherMode;
    rijndael.Padding = paddingMode;
    rijndael.KeySize = keySize;
    rijndael.BlockSize = blockSize;
    rijndael.FeedbackSize = blockSize; // no bigger than the blocksize

    rijndael.Key = derivedKey;
    rijndael.IV = iv;

    ICryptoTransform transform = rijndael.CreateDecryptor();

    byte[] decoded = Convert.FromBase64String(source);
    byte[] target = transform.TransformFinalBlock(decoded, 0, decoded.Length);

    return Encoding.UTF8.GetString(target);
}

    private static readonly byte[] iv = {
        0x30,0xA6,0x65,0xDE,0x8C,0x63,0x17,0x44,
        0xB6,0xFD,0xEA,0x5F,0x76,0xA1,0x1C,0x5F
    };

    private static readonly byte[] salt = {
        0xF9,0x39,0x0C,0xE0,0x22,0xE0,0x8E,0x84,
        0xB2,0x05,0x1E,0xA8,0x6D,0x1C,0x39,0xAC
    };

    private const int keySize = 256;
    private const int blockSize = 128;
    private const CipherMode cipherMode = CipherMode.CBC;
    private const PaddingMode paddingMode = PaddingMode.PKCS7;
    private const int derivedKeySize = 32;