Storing Secrets in .NET

Storing Secrets in .NET

Currently the .NET common language runtime and .NET Framework offer no service for storing secret information in a secure manner. If you review Chapter 7, Storing Secrets, you ll realize that storing secrets securely in software is an oxymoron, but you can raise the bar to make it harder for an attacker. And storing a password in plaintext in an XML file is not raising the bar very high!

Part of the reason for not adding this support is the .NET philosophy of XCOPY deployment. In other words, any application can be written and then deployed using simple file-copying tools. There should be no need to register DLLs or controls or to set any settings in the registry. You copy the files, and the application is live. With that in mind, you might realize that storing secrets defeats this noble goal, because you cannot store secret data without the aid of tools, because encryption uses complex algorithms and keys. However, there s no reason why, as an application developer, you cannot deploy an application after using tools to configure secret data. Or your application could use secrets but not store them. What I mean is this: your application can use and cache secret data but not persist the data, in which case XCOPY deployment is still a valid option.

If you see code like the following encryption code, file a bug and have it fixed as soon as possible:

class MyCoolCrypt { public static char[] EncryptAndDecrypt(string data) { // SSsshh!! Don t tell anyone. string key = yeKterceS"; char[] text = data.ToCharArray(); for (int i = 0; i < text.Length; i++) text[i] ^= key[i % key.Length]; return text; } }

Windows 2000 and later offer a means to protect data: the Data Protection API (DPAPI). Refer to Chapter 7 for more information about this technology. The sample code below outlines how you can use C# to create a class that interfaces with DPAPI. Note that there is another file that goes with this file, named NativeMethods.cs, which contains platform invoke definitions, data structures, and constants necessary to call DPAPI. All files are on the companion CD in the folder Secureco\Chapter 13\DataProtection.

The System.Runtime.InteropServices namespace provides a collection of classes useful for accessing COM objects and native APIs from .NET-based applications.

// DataProtection.cs namespace Microsoft.Samples.DPAPI { using System; using System.Runtime.InteropServices; using System.Text; public class DataProtection { // Protect string and return base64-encoded data. public static string ProtectData(string data, string name, int flags) { byte[] dataIn = Encoding.Unicode.GetBytes(data); byte[] dataOut = ProtectData(dataIn, name, flags); return (null != dataOut) ? Convert.ToBase64String(dataOut) : null; } // Unprotect base64-encoded data and return string. public static string UnprotectData(string data) { byte[] dataIn = Convert.FromBase64String(data); byte[] dataOut = UnprotectData(dataIn, NativeMethods.UIForbidden NativeMethods.VerifyProtection); return (null != dataOut) ? Encoding.Unicode.GetString(dataOut) : null; } //////////////////////// // Internal functions // //////////////////////// internal static byte[] ProtectData(byte[] data, string name, int dwFlags) { byte[] cipherText = null; // Copy data into unmanaged memory. NativeMethods.DATA_BLOB din = new NativeMethods.DATA_BLOB(); din.cbData = data.Length; din.pbData = Marshal.AllocHGlobal(din.cbData); Marshal.Copy(data, 0, din.pbData, din.cbData); NativeMethods.DATA_BLOB dout = new NativeMethods.DATA_BLOB(); NativeMethods.CRYPTPROTECT_PROMPTSTRUCT ps = new NativeMethods.CRYPTPROTECT_PROMPTSTRUCT(); // Fill the DPAPI prompt structure. InitPromptstruct(ref ps); try { bool ret = NativeMethods.CryptProtectData( ref din, name, NativeMethods.NullPtr, NativeMethods.NullPtr, ref ps, dwFlags, ref dout); if (ret) { cipherText = new byte[dout.cbData]; Marshal.Copy(dout.pbData, cipherText, 0, dout.cbData); NativeMethods.LocalFree(dout.pbData); } else { #if (DEBUG) Console.WriteLine( Encryption failed: + Marshal.GetLastWin32Error().ToString()); #endif } } finally { if ( din.pbData != IntPtr.Zero ) Marshal.FreeHGlobal(din.pbData); } return cipherText; } internal static byte[] UnprotectData(byte[] data, int dwFlags) { byte[] clearText = null; // Copy data into unmanaged memory. NativeMethods.DATA_BLOB din = new NativeMethods.DATA_BLOB(); din.cbData = data.Length; din.pbData = Marshal.AllocHGlobal(din.cbData); Marshal.Copy(data, 0, din.pbData, din.cbData); NativeMethods.CRYPTPROTECT_PROMPTSTRUCT ps = new NativeMethods.CRYPTPROTECT_PROMPTSTRUCT(); InitPromptstruct(ref ps); NativeMethods.DATA_BLOB dout = new NativeMethods.DATA_BLOB(); try { bool ret = NativeMethods.CryptUnprotectData( ref din, null, NativeMethods.NullPtr, NativeMethods.NullPtr, ref ps, dwFlags, ref dout); if (ret) { clearText = new byte[ dout.cbData ]; Marshal.Copy(dout.pbData, clearText, 0, dout.cbData); NativeMethods.LocalFree(dout.pbData); } else { #if (DEBUG) Console.WriteLine( Decryption failed: + Marshal.GetLastWin32Error().ToString()); #endif } } finally { if ( din.pbData != IntPtr.Zero ) Marshal.FreeHGlobal(din.pbData); } return clearText; } static internal void InitPromptstruct( ref NativeMethods.CRYPTPROTECT_PROMPTSTRUCT ps) { ps.cbSize = Marshal.SizeOf( typeof(NativeMethods.CRYPTPROTECT_PROMPTSTRUCT)); ps.dwPromptFlags = 0; ps.hwndApp = NativeMethods.NullPtr; ps.szPrompt = null; } } }

The following C# driver code shows how to use the DataProtection class:

using Microsoft.Samples.DPAPI; using System; using System.Text; class TestStub { public static void Main(string[] args) { string data = Look out for the Balrog in Moria."; string name="MySecret"; Console.WriteLine( String is: + data); string s = DataProtection.ProtectData(data, name, NativeMethods.UIForbidden); if (null == s) { Console.WriteLine( Failure to encrypt ); return; } Console.WriteLine( Encrypted Data: + s); s = DataProtection.UnprotectData(s); Console.WriteLine( Cleartext: + s); } }

As in ASP pages, do not store secret data in ASP.NET pages. If you must store them, use this code, or code such as this, instead.

You can also use COM+ construction strings. COM+ object construction enables you to specify an initialization string stored in the COM+ metadata, thereby eliminating the need to hard-code configuration information within a class. This approach is described in Chapter 12, Securing Web-Based Services. You can use functions in the System.EnterpriseServices.ServicedComponent namespace to access a construction string.



Writing Secure Code
Writing Secure Code, Second Edition
ISBN: 0735617228
EAN: 2147483647
Year: 2005
Pages: 153

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net