Поделиться через


Managed DPAPI Part II: ProtectedMemory

Last week (ok, really two weeks ago ....), I wrote about using DPAPI with Whidbey.  (You can find that post here: Managed DPAPI Part I: ProtectedData).  In addition to the ProtectedData class, Whidbey will also expose DPAPI through the ProtectedMemory class.  In this post I'll show some samples of using ProtectedMemory and compare it to ProtectedData to help in making decisions as to which one to use in your managed application.

ProtectedMemory is a wrapper around CryptProtectMemory and CryptUnprotectMemory.  The main difference between ProtectedMemory and ProtectedData is that if the computer is rebooted between calls to Protect and Unprotect, you won't be able to decrypt the data.  This means that if you need to encrypt some data and save it to a file or other persistent storage, you'll need to use ProtectedData instead.  However if the data you're encrypting is for use just while the application is running, ProtectedMemory is slightly easier to use, and is a good choice.

Like the ProtectedData class, ProtectedMemory contains two static methods Protect and Unprotect.  Since there's no MSDN docs for Whidbey yet, I'll provide their signatures here:

void Protect(byte[] userData, MemoryProtectionScope scope)
userData memory area to encrypt
scope what applications can decrypt

The corresponding Unprotect method is:

void Unprotect(byte[] encryptedData, MemoryProtectionScope scope)
encryptedData data to decrypt
scope must match scope from call to Protect

As you can see, ProtectedMemory is even simpler than ProtectedData.  The encrypted data overwrites the original data, so you don't have to worry about destroying your original array.  Also, there's no entropy to deal with in these APIs.  The scope parameter has one of the following three values:

MemoryProtectionScope.SameProcess Only the current process will be able to decrypt the data
MemoryProtectionScope.CrossProcess Applications running in different processes can decrypt the data
MemoryProtectionScope.SameLogon Applications running in different processes, but running as the same user, can decrypt the data.

Generally you'll want to use MemoryProtectionScope.SameProcess.  When unprotecting your data, you'll need to provide the same MemoryProtectionScope, otherwise the decryption won't work.  Also, if you're writing a server application that recieves some data from a user application (say encrypted with MemoryProtectionScope.SameLogon), you'll need to impersonate the user that the other process is running as before decrypting.

One slight gotchya that exists with ProtectedMemory is that it will only work on blocks of 16 bytes, so you may have to pad your array before using it.  Here's some sample code:

byte[] sensitiveData = new byte[]
{
    0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
    0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10
};

Console.WriteLine("Before protection:");
PrintBytes(sensitiveData); // utility function that simply prints out the byte array -- not part of the framework
        
ProtectedMemory.Protect(sensitiveData, MemoryProtectionScope.SameProcess);

Console.WriteLine("After protection:");
PrintBytes(sensitiveData);

ProtectedMemory.Unprotect(sensitiveData, MemoryProtectionScope.SameProcess);
        
Console.WriteLine("After unprotection:");
PrintBytes(sensitiveData)

The output from this code is:

Before protection:
01, 02, 03, 04, 05, 06, 07, 08, 09, 0A
0B, 0C, 0D, 0E, 0F, 10
After protection:
16, 9A, B1, A7, 3E, 09, 1B, 5F, FF, 30
B7, 64, 90, 4B, 15, FC
After unprotection:
01, 02, 03, 04, 05, 06, 07, 08, 09, 0A
0B, 0C, 0D, 0E, 0F, 10

There are some things to be aware of when using ProtectedMemory. Since your data needs to be decrypted for you to use it, there will be some window of time that it exists in plaintext in memory and can be found with a debugger.  During the time that your array is unencrypted, you're also vulnerable to some other problems.  Since managed arrays are under the control of the garbage collector, if they aren't pinned, they may be moved around memory.  In this case, even though you've protected your copy of the array, other unencrypted “images” of the sensitive data may still exist.  And of course, there's the possibility that Windows swaps your process out of memory into the swap file while your data is unencrypted.  In this case, an attacker could read the swap file and possibly find your unencrypted data.

ProtectedMemory makes it very easy to significantly raise the bar for an attacker to spy on your process and find some sensitive data.  Its use is very easy, and I recommend employing it in your Whidbey applications where you'll be holding information that you want hidden from prying eyes.  It's not perfect, but when used properly, attackers will have a significantly harder time obtaining sensitive information from your application.

As for choosing between ProtectedMemory and ProtectedData:

Data use ProtectedData ProtectedMemory System.Security.Cryptography algorithms
Written to a file x x
Stored in a database x x
Only used inside your application x x
Used in multiple applications, but not stored anywhere x x
Transmitted over the network x

Comments

  • Anonymous
    May 17, 2004
    Does windows make a lot of use of this? A concern I have that I see no end-user workaround is: What if an attacker shuts off my machine by pulling the power, and then steals the machine? Will they be able to gain access to any of the DPAPI protected things (since the pagefile won't be cleared like it is on a nice shutdown)?

  • Anonymous
    May 17, 2004
    I'm not sure how much Windows uses CryptProtectMemory internally, Michael Howard's blog might be a better place to find that out (http://blogs.msdn.com/michael_howard). As to your other concern, that threat is mitigated by the fact that data protected by CryptProtectMemory (and hence ProtectedMemory), cannot be decrypted across boots of Windows. The reason for this is that the base key is regenerated every time Windows boots up. Even if the attacker could get the encrypted data out of the swap file, they'd be unable to use CryptUnprotectMemory to decrypt it. They would also be unable to fool your app into calling CryptUnprotectMemory to decrypt the data.

  • Anonymous
    April 10, 2006
    Hatteras has three tiers: client, middle, and data.  The middle tier is an ASP.NET web service on...

  • Anonymous
    September 14, 2006
    Anoymous method
    http://www.theserverside.net/tt/articles/showarticle.tss?id=AnonymousMethods
    Generic...

  • Anonymous
    October 31, 2006
    http://msdn2.microsoft.com/en-us/library/aa302406.aspx http://blogs.msdn.com/shawnfa/archive/2004/05/17/133650.asp

  • Anonymous
    February 04, 2007
    I use WindowsIdentity.Impersonate() to log on as a different user and protect data. And ProtectedData.Protect() raises CryptographicException "The system can not find the file specified". What can that mean? Same protection code is used when crypting by current user, and that goes ok.

  • Anonymous
    February 20, 2007
    Hi Kurt, DPAPI stores information in the user's profile, so in order to use it when you've impersonated another user you'll need to ensure that their entire profile is loaded.  You can do this by calling LoadUserProfile after you've called LogonUser. -Shawn

  • Anonymous
    May 02, 2007
    I get the same error as Kurt when I use Impersonate() on a web page (C#):        this.Context.Request.LogonUserIdentity.Impersonate();        return Convert.ToBase64String(ProtectedData.Protect(Encoding.UTF8.GetBytes(unencrypted), null, DataProtectionScope.CurrentUser)); If this problem is caused by the same issue as Kurt, how can I load the user profile from the web page

  • Anonymous
    May 07, 2007
    Hi Kevin, You can use the LoadUserProfile API to force the user's profile to be loaded. -Shawn

  • Anonymous
    April 22, 2009
    Every time you need to store sensitive data your first thought use to be encryption. You probably gather