Security is a multidimensional issue, security risks can come from anywhere. You can trust all code that runs on your network, give any user access to important files, and never bother to check that code on your machine has not changed. You can run without virus protection software, not build security into your own code, and give too many privileges to too many accounts. You can even use a number of built-in functions carelessly enough to allow break-ins, and you can leave server ports open and unmonitored. Obviously, the list continues to grow. What are some of the really important issues, the biggest mistakes you should watch out for right now so that you don't compromise your data or your system? .
Unfortunately developers tend to omit security requirements epically if the system being developed is an intranet system; internal security threats can be more deadly than external ones. The majority of security breaches involve internal employees, with some estimates as high as 85%.
Through this talk I will concentrate on securing application configuration files as these files often contain sensitive data such as user names, passwords, connection strings and encryption keys. Storing data securely in a configuration system is not an easy problem to solve. While this particular feature, secure connection string storage, looked as if it wouldn’t get done. A whole host of problems surrounding it, such as key storage, were in the way. I come across a lot of applications that use custom encryption techniques and at runtime these application deals with configuration decryption. The good news is that not only was it eventually completed, but it has become part of the powerful new set of APIs in .Net 2.0 that allow you to manage the configuration file programmatically.
.Net APIs integrates with DPAPI (Windows Data Protection application programming interface) to encrypt section of the application configuration file, this technique is called protected sections. Of course, Encrypting and decrypting data incurs performance overhead. To keep this overhead to a minimum, encrypt only the sections of your configuration file that store sensitive data.
The sections that usually contain sensitive information that you need to encrypt are the following:
- <appSettings>. This section contains custom application settings.
- <connectionStrings>. This section contains connection strings.
- <Identity>. This section can contain impersonation credentials.
- <sessionState>. This section contains the connection string for the out-of-process session state provider.
- <Custom>. The section that is customized by developers and may contain sensitive data.
Before digging into “Protected Section” technique let’s take a look to DPAPI, the Data Protection API (DPAPI) is a pair of function calls that provide OS-level data protection services to user and system processes. By OS-level, we mean a service that is provided by the operating system itself and does not require any additional libraries. By data protection, we mean a service that provides confidentiality of data through encryption. DPAPI is a password-based data protection service: it requires a password to provide protection, DPAPI works by generating a key from the current user's credentials (generally their password). It then generates a master key, and encrypts this with the key generated by the user's credentials. A random session key is created for each call. This key is derived from the master key, some random data, and some optional entropy passed in by the user. The session key is then used to do the actual encryption. Rather than storing the session key, the random data used in key creation is stored in the encrypted output.
For added security, the master key expires every few months. When this happens, a new master key is generated, but the old one is retained for use in decrypting any data that it had previously encrypted. This limits the amount of data that an attacker who has compromised a single master key can decrypt. Notice how the entire system is based on the current user's password. What happens when you change your password? DPAPI will hook the password changing event, and re-encrypt the master keys using the new password.The master key material is managed in something called a key container. This container abstraction allows you to use a variety of crypto providers, including some that use hardware devices, like smart cards, to store key material. You can have as many key containers as you need, limited only by the storage space on the device you're using.
Since key management plays major rule in the context, we will need a key manager that allows our administrator to create, delete, enumerate and manipulate permission on key containers for a certain machine. Unfortunately .Net does not provide any APIs to handle key management so we will start creating our own DPAPI wrapper for .Net.
The first step in creating our key manager is to identify DPAPIs and windows APIs that will be called from managed code:
| API Function |
Description |
| CryptAcquireContext |
Used to acquire a handle to a particular key container within a particular cryptographic service provider(CSP). |
| CryptReleaseContext |
Releases the handle of a cryptographic service provider (CSP) and a key container |
| CryptGetProvParam |
Retrieves parameters that govern the operations of a cryptographic service provider (CSP). |
| SHObjectProperties |
Invokes the Properties context menu command on a Shell object. |
I will use Microsoft P/Invoke Interop Assistant to generate managed code signature of the above APIs, you can find this tool here.This tool will generate the managed code signature for the above APIs:
[System.Runtime.InteropServices.DllImportAttribute("advapi32.dll", EntryPoint = "CryptAcquireContext")]
[return: System.Runtime.InteropServices.MarshalAsAttribute(System.Runtime.InteropServices.UnmanagedType.Bool)]
public static extern bool CryptAcquireContext(ref uint phProv,
[System.Runtime.InteropServices.InAttribute()]
[System.Runtime.InteropServices.MarshalAsAttribute(System.Runtime.InteropServices.UnmanagedType.LPStr)] string szContainer,
[System.Runtime.InteropServices.InAttribute()]
[System.Runtime.InteropServices.MarshalAsAttribute(System.Runtime.InteropServices.UnmanagedType.LPStr)] string szProvider,
uint dwProvType,
uint dwFlags);
After generating managed code signature we will create some static method to wrap the APIs, for example the following code snippet will check if a certain key container exists or not:
private static bool KeyContainerExists(string keyContainerName)
{
uint phProv = 0;
if (CryptAcquireContext(ref phProv, keyContainerName, null, PROV_RSA_FULL, CRYPT_MACHINE_KEYSET))
{
if (!CryptReleaseContext(phProv, 0))
{
throw CreateWin32Error(System.Runtime.InteropServices.Marshal.GetLastWin32Error());
}
return true;
}
else return false;
}
By wrapping all the APIs, we will have a key container manager that is capable of:
- Enumerate all RSA key containers.
- Import / Export RSA Keys.
- Set permission for a certain key container.
- Create new key into a certain container.
- Delete Key from a certain container.
You can find the complete Key Container Manager code here. KeyContainerManager.zip (2.27 kb)
After creating our key container administrator let’s move to our main target (Protecting Configuration). Protected configuration enables you encrypt sensitive information in your configuration file. In order to do that you will need protected configuration provider, .Net 2.0 and later versions includes the following protection providers:
- RSAProtectedConfigurationProvider. This is the default provider and uses the RSA public key encryption to encrypt and decrypt data.
- DPAPIProtectedConfigurationProvider. This provider uses the Windows Data Protection API (DPAPI) to encrypt and decrypt data.
You can create your own custom protected configuration provider by inheriting from ProtectedConfigurationProvider class, creating custom provider is out of talk scope instead I will use RSAProtectedConfigurationProvider because it allows you to export RSA keys which in turn can be used in multiple servers in your server farm. If you encrypted your application configuration on a certain server and you want to load balance this server then you need to copy this encrypted configuration file to the new server and export encryption key to it, at runtime .Net framework automatically decrypts configuration sections when processing them; therefore, you do not need to write any additional decryption code.
The RSAProtectedConfigurationProvider supports machine-level and user-level key containers for key storage. Machine-level key containers are available to all users, but a user-level key container is available to that user only. The choice of container depends largely on whether or not your application shares a server with other applications and whether or not sensitive data must be kept private for each application. Microsoft suggests to use a machine-level key container in the following situations:
- Your application runs on its own dedicated server with no other applications.
- You have multiple applications on the same server and you want those applications to be able to share sensitive information and the same encryption key.
I will use machine key container to allow multi-server deployment and if you want to limit sensitive information sharing then you can use our Key container manager to create a key container per application and assign necessary permissions.
Now let’s follow the steps needed to encrypt our configuration file using RSA provider:
1- The first thing you need to do is to choose your RSA encryption key container, you can use Key Container manager to enumerate all keys or create a new one. The following example shows how to create a new key container:
KeyContainerManager m_manager = new KeyContainerManager();
m_manager.CreateNew("MyCustomKey", 1024);
N.B: 1024 is the key size in Bits.
2- Add and configure a custom protected configuration provider. To do this, add the following <configProtectedData> section to the configuration file. Note that the key container name is set to "MyCustomKey", which is the name of the key container created previously:
<configProtectedData>
<providers>
<add useMachineContainer="false" keyContainerName="MyCustomKey" name="MyCustomKeyProvider" type="System.Configuration.RsaProtectedConfigurationProvider,System.Configuration, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" />
</providers>
</configProtectedData>
3- Identify which configuration section need to be encrypted, the following sections can be encrypted using protected configuration provider:
- <processModel>.
- <runtime>.
- <mscorlib>.
- <startup>.
- <system.runtime.remoting>.
- <configProtectedData>.
- <satelliteassemblies>.
- <cryptographySettings>.
- <cryptoNameMapping>.
- <cryptoClasses>.
If you have sensitive information in these section, you can refer to Microsoft knowledge base 329290 which discuss in details how to encrypt these sections.
4- After choosing your section, all you need is a single call to ProtectSection to do the actual encryption of the specified section. The following snippet show how to encrypt your connection strings:
ConfigurationSection section = (ConfigurationSection)ConfigurationManager.GetSection("connectionStrings");
section.SectionInformation.ProtectSection("MyCustomKeyProvider");
5- At runtime .Net will decrypt the section to your application level without any interference from your side, although you can decrypt this section to be plain text as follows:
ConfigurationSection section = (ConfigurationSection)ConfigurationManager.GetSection("connectionStrings");
section.SectionInformation.UnprotectSection();
6- Optionally, you can use Key Container Manager to export “MyCustomKey” and import this key to another server to use the same encrypted configuration file on the other server as follows:
KeyContainerManager m_manager = new KeyContainerManager();
m_manager.ExportKeyContainer(@"c:\MyCustomKey.xml", "MyCustomKey", true);
Then import it to the other server:
KeyContainerManager m_manager = new KeyContainerManager();
m_manager.ImportKeyContainer(@"c:\MyCustomKey.xml", "MyCustomKey");
I creeated a configuration management tool to automate all the above steps, here is a screenshot of this tool:
By using this tool you can mainplute key containers, you can edit your confgiuration and of course you can encrypt/decrypt any section of your application configuration file. ConfigurationManager.zip (2.19 mb)
Currently rated 4.5 by 4 people
- Currently 4.5/5 Stars.
- 1
- 2
- 3
- 4
- 5