Aracılığıyla paylaş


Application Password Security

So you have written your neat application that uses WCF hopefully using the peer networking stack (aka PeerChannel) and you are all ready to distribute your application. Now, this is a good time to pause and think about a few things.

With all the connected applications that WCF will make easy to develop and distribute it is imperative on you (the application developers') part to map out the lifecycle of an application password. And by lifecycle I mean the followind -
1. secure generation of a strong password,
2. secure storage of the password,
3. secure retrieval of the password, and
4. secure transmission (4.a electronic and 4.b otherwise) of the password.

In this article we will evaluate and implement the code for the first 3 points listed above. 4.a has been covered very well by RamP in his blog entry "PeerChannel's security", while 4.b. is outside the scope of this article, and is a potential future blog entry candidate.

Item 1 can be tackled in a variety of ways, however, we will look at the approach that is exposed by the .Net Framework 2.0 libraries. Items 2 and 3 can be easily implemented using the Windows Credential Management API infrastructure (aka CredMan). We will be using the CredRead, CredWrite (and the associated CredFree) APIs. We recommend that you read through the AuthN APIs section of Windows Security Infrastructure to get a fuller understanding of the breadth and scope of this infrastructure.

The nitty gritty

What we are going to do is take an existing application and enhance it so it can securely mange the lifecycle of the password. We will use SecureChat sample that will be shipped with WinFX SDK Beta 2 for this purpose. You may wish to check out the original PeerChannel Chat sample here to get an idea of the structure of the application. Or check out Kevin Ransoms blog for a great start on PeerChannel programming.

Developing the CredManUtils library

We will start with the development of the managed CredMan library (lets call it CredManUtils) that provides a safe wrapper around the native methods (CredRead, CredWrite and CredFree). Most of the work in these wrappers will be around safely stuffing data into & safely retrieving data out of the CREDENTIAL structure. A standard practice in this type of interop programming is to define a managed structure that mirrors the native strucuture to the byte and then marshal the data back and forth between the native and managed structures. The application programs the managed structure (Credential) while the native APIs interacts with the native structure (NativeCredential). Since we will have to explicitly allocate and deallocate memory when we interact with native APIs, we should wrap the alloc and free semantics inside a CriticalHandle. By overriding the abstract method ReleaseHandle with the memory freeing code, we can be sure that when the object is disposed the credential memory is CredFree'd accordingly.

The CREDENTIAL structure will be configured using a number of enums that define the Type of the credential, the scope of Persistance of the credential. The enums are listed towards the end of this article.

Code Dirt

So much talk about the CredMan APIs that its now time to take a look at their signatures and how we invoke them. As you can see these native APIs are using the NativeCredential structure as mentioned earlier.

[DllImport("Advapi32.dll", EntryPoint = "CredReadW", CharSet = CharSet.Unicode, SetLastError = true)]
static extern bool CredRead(string target, CRED_TYPE type, int reservedFlag, out IntPtr CredentialPtr);

[DllImport("Advapi32.dll", EntryPoint = "CredWriteW", CharSet = CharSet.Unicode, SetLastError = true)]
static extern bool CredWrite([In] ref NativeCredential userCredential, [In] UInt32 flags);

[DllImport("Advapi32.dll", EntryPoint = "CredFree", SetLastError = true)]
static extern bool CredFree([In] IntPtr cred);

Here is the native credential strucure that is used in the signatures -

[

StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
private struct NativeCredential
{
   public UInt32 Flags;
   public CRED_TYPE Type;
   public IntPtr TargetName;
   public IntPtr Comment;
   public System.Runtime.InteropServices.ComTypes.FILETIME LastWritten;
   public UInt32 CredentialBlobSize;
   public IntPtr CredentialBlob;
   public UInt32 Persist;
   public UInt32 AttributeCount;
   public IntPtr Attributes;
   public IntPtr TargetAlias;
   public IntPtr UserName;

/// <summary>
/// This method derives a NativeCredential instance from a given Credential instance.
/// </summary>
/// <param name="cred">The managed Credential counterpart containing data to be stored.</param>
/// <returns>A NativeCredential instance that is derived from the given Credential
/// instance.</returns>
internal static NativeCredential GetNativeCredential(Credential cred)
{
      NativeCredential ncred = new NativeCredential();
ncred.AttributeCount = 0;
ncred.Attributes = IntPtr.Zero;
ncred.Comment = IntPtr.Zero;
ncred.TargetAlias = IntPtr.Zero;
ncred.Type = CRED_TYPE.GENERIC;
ncred.Persist = (UInt32)Persistance.CRED_PERSIST_SESSION;
      ncred.CredentialBlobSize = (UInt32)cred.CredentialBlobSize;
ncred.TargetName = Marshal.StringToCoTaskMemUni(cred.TargetName);
ncred.CredentialBlob = Marshal.StringToCoTaskMemUni(cred.CredentialBlob);
ncred.UserName = Marshal.StringToCoTaskMemUni(System.Environment.UserName);
      return ncred;
}
}

And here is the Credential type used by the managed code -

[

StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
private struct Credential
{
   public UInt32 Flags;
   public CRED_TYPE Type;
   public string TargetName;
   public string Comment;
   public System.Runtime.InteropServices.ComTypes.FILETIME LastWritten;
   public UInt32 CredentialBlobSize;
   public string CredentialBlob;
   public Persistance Persist;
   public UInt32 AttributeCount;
   public IntPtr Attributes;
   public string TargetAlias;
   public string UserName;
}

As you can see, the difference between the Credential and NativeCredential types is essentially in the way the datatypes of the fields are declared. The managed Credential type uses friendly enum names, and strings, while the native counterpart deals in UInt32s and IntPtrs.

Now comes the critial part. This type provides a safe way to deal with (and importantly dispose of) Win32 native memory resource handles.

#region

Critical Handle Type definition
sealed class CriticalCredentialHandle : CriticalHandleZeroOrMinusOneIsInvalid
{
// Set the handle.
internal CriticalCredentialHandle(IntPtr preexistingHandle)
{
SetHandle(preexistingHandle);
}

internal Credential GetCredential()
{
if (!IsInvalid)
{
// Get the Credential from the mem location
NativeCredential ncred = (NativeCredential)Marshal.PtrToStructure(handle,
typeof(NativeCredential));

// Create a managed Credential type and fill it with data from the native counterpart.
Credential cred = new Credential();
cred.CredentialBlobSize = ncred.CredentialBlobSize;
cred.CredentialBlob = Marshal.PtrToStringUni(ncred.CredentialBlob,
(int)ncred.CredentialBlobSize / 2);
cred.UserName = Marshal.PtrToStringUni(ncred.UserName);
cred.TargetName = Marshal.PtrToStringUni(ncred.TargetName);
cred.TargetAlias = Marshal.PtrToStringUni(ncred.TargetAlias);
cred.Type = ncred.Type;
cred.Flags = ncred.Flags;
cred.Persist = (Persistance)ncred.Persist;
return cred;
}
else
{
throw new InvalidOperationException("Invalid CriticalHandle!");
}
}

// Perform any specific actions to release the handle in the ReleaseHandle method.
// Often, you need to use Pinvoke to make a call into the Win32 API to release the
// handle. In this case, however, we can use the Marshal class to release the unmanaged memory.

override protected bool ReleaseHandle()
{
// If the handle was set, free it. Return success.
if (!IsInvalid)
{
// NOTE: We should also ZERO out the memory allocated to the handle, before free'ing it
// so there are no traces of the sensitive data left in memory.
CredFree(handle);
// Mark the handle as invalid for future users.
SetHandleAsInvalid();
return true;
}
// Return false.
return false;
}
}
#endregion

The WriteCred and ReadCred API helper methods are fairly straightforward. They deal with safely trolling data between the managed and native Credential structures and this keeps the helpers simple and easy to understand.

public static int WriteCred(string key, string secret)
{
// Validations.

byte[] byteArray = Encoding.Unicode.GetBytes(secret);
if (byteArray.Length > 512)
throw new ArgumentOutOfRangeException("The secret message has exceeded 512 bytes.");

// Go ahead with what we have are stuff it into the CredMan structures.
Credential cred = new Credential();
cred.TargetName = key;
cred.CredentialBlob = secret;
cred.CredentialBlobSize = (UInt32)Encoding.Unicode.GetBytes(secret).Length;
cred.AttributeCount = 0;
cred.Attributes = IntPtr.Zero;
cred.Comment = null;
cred.TargetAlias = null;
cred.Type = CRED_TYPE.GENERIC;
cred.Persist = Persistance.CRED_PERSIST_SESSION;
NativeCredential ncred = NativeCredential.GetNativeCredential(cred);
// Write the info into the CredMan storage.
bool written = CredWrite(ref ncred, 0);
int lastError = Marshal.GetLastWin32Error();
if (written)
{
return 0;
}
else
{
string message = string.Format("CredWrite failed with the error code {0}.", lastError);
throw new Exception (message);
}
}

And here is the ReadCred method, simple and straightforward. Note that the code to free the returned NativeCredential structure is present in the critical handle instance which is implicitly invoked when the object is to be disposed.

public static string ReadCred(string key)
{
   // Validations.

   IntPtr nCredPtr;
   string readPasswordText = null;

   // Make the API call using the P/Invoke signature
   bool read = CredRead (key, CRED_TYPE.GENERIC, 0, out nCredPtr);
   int lastError = Marshal.GetLastWin32Error();

   // If the API was successful then...
   if (read)
   {
      using (CriticalCredentialHandle critCred = new CriticalCredentialHandle(nCredPtr))
      {
         Credential cred = critCred.GetCredential();
         readPasswordText = cred.CredentialBlob;
      }
   }
   else
   {
      string message = string.Format("ReadCred failed with the error code {0}.", lastError);
      throw new Exception (message);
   }
   return readPasswordText;
}

The above code snippets show us how to write and read passwords passwords to/from a secure storage.

Let there be a strong password!

Let us now consider the initial stage in the password lifecycle - The Generation of A Strong Password. The definition of a strong password is a moving target and is a subject of much debate. We will not dwelve into the details of this debate (at this time :) ) and instead focus on leveragingt the functionality provided by the amazing .Net framework 2.0. One of the newest and more-wanted features of .Net 2.0 is the System.Web.Security.Membership class. This class can be used to validate user credentials and manage user settings. While we do not deal with "user" info except passwords here, this class is nevertheless very useful even in non ASP.NET scenarios.

The GeneratePassword static method of this type is what we will be using in this sample. This method takes 2 parameters (the overall length of the password and the no. of non-alphanumeric chars *required* to be present in the generated password.

Here is how we will define a helper method that uses this GeneratePassword API -

internal static string GetRandomPassword()
{
   int length = 15;
   int numberOfNonAlphanumericCharacters = 5;

   string password = System.Web.Security.Membership.GeneratePassword(length,
numberOfNonAlphanumericCharacters);

   return password;
}

Although it does not generate the most secure password, this is a great step in the right direction for all applications that need to generate passwords.

Secure password storage & retrieval - Check.
Auto generation of strong password - Check.
Is the user supplied password Strong? - Check.

Now we have the ability to have the application generate strong password, store and retrieve this password from a secure storage. In many cases, the application would have to allow the user to specify a password that can be used instead of the app generated password. Since the security chain is only as strong as the weakest link the application will need to validate and check if it conforms to the application password strength policy. If the password is weak then the user is not allowed to create a secure chat mesh.

A quick fire way to check for password policy conformance is to use regular expressions to validate the user supplied password according to our app password rules.

internal static bool IsPasswordStrong (string password)
{
   // So what we mean by 'strong' here is that the password satisfy the following rules -
   // a. Have atleast 15 characters
   // b. Have atleast 3 digits
   // c. Have atleast 2 special chars.

   System.Text.RegularExpressions.Regex strongEx =
      new System.Text.RegularExpressions.Regex(@"(?=.{15,})(?=(.*\d){3,})(?=(.*\W){2,})");

   return strongEx.IsMatch(password);
}

If the password is deemed strong then the user is allowed to create a secure chat mesh otherwise the app will indicate the reason and quit.

Finishing up the App

The final touches added to the sample application include - a neat command line interface (CLI) switch processing. The application in its current avatar runs from the command line and accepts the following parameters -

  • generatePassword - This switch instructs the app to generate a secure random password.

  • password:<value> - This switch allows the user to specify a password. This password is validated for conformance to the app password policy. If the password is deemed 'weak' then the application will mention the reason and quit.

  • storePassword - This switch instructs the app to store the password (user supplied, or generated) in the CredMan storage.

  • readPassword - This switch instructs the app to read the password already stored in the CredMan storage. (typically used in later app runs).

 

Wrapping up.

The major components of the sample application have been broken down and explained above. We have seen how to use the Windows Security infrastructure to store and retrieve sensitive information (passwords, et al) from the Credential Manager storage, as well as easy .NET 2.0 provided ways to generate random strong passwords that conform to the applications password policy. And finally, a quickfire way to validate a supplied password and check for its conformance to the application password policy.

These helper methods should give you folks an idea on how to leverage the Windows Security infrastructure and using it in conjunction with the PeerChannel app programming.

Comments

  • Anonymous
    June 18, 2009
    PingBack from http://thestoragebench.info/story.php?id=713

  • Anonymous
    December 20, 2011
    Hey team, this is a great example of how to use the credential manager from .NET. Thanks for the article.

  • Anonymous
    September 25, 2013
    This article is completely broken. Links don't work and only presents about 40% of the required code.